diff options
author | David S. Miller <davem@sunset.davemloft.net> | 2007-10-12 05:08:29 +0400 |
---|---|---|
committer | David S. Miller <davem@sunset.davemloft.net> | 2007-10-12 05:08:29 +0400 |
commit | 6f535763165331bb91277d7519b507fed22034e5 (patch) | |
tree | 1968a01affa1cce3a3199c455d1fe1ebdca3ff47 /drivers/net/tg3.c | |
parent | b08d6cb22c777c8c91c16d8e3b8aafc93c98cbd9 (diff) | |
download | linux-6f535763165331bb91277d7519b507fed22034e5.tar.xz |
[NET]: Fix NAPI completion handling in some drivers.
In order for the list handling in net_rx_action() to be
correct, drivers must follow certain rules as stated by
this comment in net_rx_action():
/* Drivers must not modify the NAPI state if they
* consume the entire weight. In such cases this code
* still "owns" the NAPI instance and therefore can
* move the instance around on the list at-will.
*/
A few drivers do not do this because they mix the budget checks
with reading hardware state, resulting in crashes like the one
reported by takano@axe-inc.co.jp.
BNX2 and TG3 are taken care of here, SKY2 fix is from Stephen
Hemminger.
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net/tg3.c')
-rw-r--r-- | drivers/net/tg3.c | 55 |
1 files changed, 37 insertions, 18 deletions
diff --git a/drivers/net/tg3.c b/drivers/net/tg3.c index d2b30fb0b318..a402b5c01896 100644 --- a/drivers/net/tg3.c +++ b/drivers/net/tg3.c @@ -3555,12 +3555,9 @@ next_pkt_nopost: return received; } -static int tg3_poll(struct napi_struct *napi, int budget) +static int tg3_poll_work(struct tg3 *tp, int work_done, int budget) { - struct tg3 *tp = container_of(napi, struct tg3, napi); - struct net_device *netdev = tp->dev; struct tg3_hw_status *sblk = tp->hw_status; - int work_done = 0; /* handle link change and other phy events */ if (!(tp->tg3_flags & @@ -3578,11 +3575,8 @@ static int tg3_poll(struct napi_struct *napi, int budget) /* run TX completion thread */ if (sblk->idx[0].tx_consumer != tp->tx_cons) { tg3_tx(tp); - if (unlikely(tp->tg3_flags & TG3_FLAG_TX_RECOVERY_PENDING)) { - netif_rx_complete(netdev, napi); - schedule_work(&tp->reset_task); + if (unlikely(tp->tg3_flags & TG3_FLAG_TX_RECOVERY_PENDING)) return 0; - } } /* run RX thread, within the bounds set by NAPI. @@ -3590,21 +3584,46 @@ static int tg3_poll(struct napi_struct *napi, int budget) * code synchronizes with tg3->napi.poll() */ if (sblk->idx[0].rx_producer != tp->rx_rcb_ptr) - work_done = tg3_rx(tp, budget); + work_done += tg3_rx(tp, budget - work_done); - if (tp->tg3_flags & TG3_FLAG_TAGGED_STATUS) { - tp->last_tag = sblk->status_tag; - rmb(); - } else - sblk->status &= ~SD_STATUS_UPDATED; + return work_done; +} - /* if no more work, tell net stack and NIC we're done */ - if (!tg3_has_work(tp)) { - netif_rx_complete(netdev, napi); - tg3_restart_ints(tp); +static int tg3_poll(struct napi_struct *napi, int budget) +{ + struct tg3 *tp = container_of(napi, struct tg3, napi); + int work_done = 0; + + while (1) { + work_done = tg3_poll_work(tp, work_done, budget); + + if (unlikely(tp->tg3_flags & TG3_FLAG_TX_RECOVERY_PENDING)) + goto tx_recovery; + + if (unlikely(work_done >= budget)) + break; + + if (likely(!tg3_has_work(tp))) { + struct tg3_hw_status *sblk = tp->hw_status; + + if (tp->tg3_flags & TG3_FLAG_TAGGED_STATUS) { + tp->last_tag = sblk->status_tag; + rmb(); + } else + sblk->status &= ~SD_STATUS_UPDATED; + + netif_rx_complete(tp->dev, napi); + tg3_restart_ints(tp); + break; + } } return work_done; + +tx_recovery: + netif_rx_complete(tp->dev, napi); + schedule_work(&tp->reset_task); + return 0; } static void tg3_irq_quiesce(struct tg3 *tp) |