diff options
author | David S. Miller <davem@sunset.davemloft.net> | 2007-10-11 21:08:29 -0400 |
---|---|---|
committer | David S. Miller <davem@sunset.davemloft.net> | 2007-10-11 21:08:29 -0400 |
commit | 6f535763165331bb91277d7519b507fed22034e5 (patch) | |
tree | 1968a01affa1cce3a3199c455d1fe1ebdca3ff47 /drivers/net/tg3.c | |
parent | b08d6cb22c777c8c91c16d8e3b8aafc93c98cbd9 (diff) |
[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: | |||
3555 | return received; | 3555 | return received; |
3556 | } | 3556 | } |
3557 | 3557 | ||
3558 | static int tg3_poll(struct napi_struct *napi, int budget) | 3558 | static int tg3_poll_work(struct tg3 *tp, int work_done, int budget) |
3559 | { | 3559 | { |
3560 | struct tg3 *tp = container_of(napi, struct tg3, napi); | ||
3561 | struct net_device *netdev = tp->dev; | ||
3562 | struct tg3_hw_status *sblk = tp->hw_status; | 3560 | struct tg3_hw_status *sblk = tp->hw_status; |
3563 | int work_done = 0; | ||
3564 | 3561 | ||
3565 | /* handle link change and other phy events */ | 3562 | /* handle link change and other phy events */ |
3566 | if (!(tp->tg3_flags & | 3563 | if (!(tp->tg3_flags & |
@@ -3578,11 +3575,8 @@ static int tg3_poll(struct napi_struct *napi, int budget) | |||
3578 | /* run TX completion thread */ | 3575 | /* run TX completion thread */ |
3579 | if (sblk->idx[0].tx_consumer != tp->tx_cons) { | 3576 | if (sblk->idx[0].tx_consumer != tp->tx_cons) { |
3580 | tg3_tx(tp); | 3577 | tg3_tx(tp); |
3581 | if (unlikely(tp->tg3_flags & TG3_FLAG_TX_RECOVERY_PENDING)) { | 3578 | if (unlikely(tp->tg3_flags & TG3_FLAG_TX_RECOVERY_PENDING)) |
3582 | netif_rx_complete(netdev, napi); | ||
3583 | schedule_work(&tp->reset_task); | ||
3584 | return 0; | 3579 | return 0; |
3585 | } | ||
3586 | } | 3580 | } |
3587 | 3581 | ||
3588 | /* run RX thread, within the bounds set by NAPI. | 3582 | /* run RX thread, within the bounds set by NAPI. |
@@ -3590,21 +3584,46 @@ static int tg3_poll(struct napi_struct *napi, int budget) | |||
3590 | * code synchronizes with tg3->napi.poll() | 3584 | * code synchronizes with tg3->napi.poll() |
3591 | */ | 3585 | */ |
3592 | if (sblk->idx[0].rx_producer != tp->rx_rcb_ptr) | 3586 | if (sblk->idx[0].rx_producer != tp->rx_rcb_ptr) |
3593 | work_done = tg3_rx(tp, budget); | 3587 | work_done += tg3_rx(tp, budget - work_done); |
3594 | 3588 | ||
3595 | if (tp->tg3_flags & TG3_FLAG_TAGGED_STATUS) { | 3589 | return work_done; |
3596 | tp->last_tag = sblk->status_tag; | 3590 | } |
3597 | rmb(); | ||
3598 | } else | ||
3599 | sblk->status &= ~SD_STATUS_UPDATED; | ||
3600 | 3591 | ||
3601 | /* if no more work, tell net stack and NIC we're done */ | 3592 | static int tg3_poll(struct napi_struct *napi, int budget) |
3602 | if (!tg3_has_work(tp)) { | 3593 | { |
3603 | netif_rx_complete(netdev, napi); | 3594 | struct tg3 *tp = container_of(napi, struct tg3, napi); |
3604 | tg3_restart_ints(tp); | 3595 | int work_done = 0; |
3596 | |||
3597 | while (1) { | ||
3598 | work_done = tg3_poll_work(tp, work_done, budget); | ||
3599 | |||
3600 | if (unlikely(tp->tg3_flags & TG3_FLAG_TX_RECOVERY_PENDING)) | ||
3601 | goto tx_recovery; | ||
3602 | |||
3603 | if (unlikely(work_done >= budget)) | ||
3604 | break; | ||
3605 | |||
3606 | if (likely(!tg3_has_work(tp))) { | ||
3607 | struct tg3_hw_status *sblk = tp->hw_status; | ||
3608 | |||
3609 | if (tp->tg3_flags & TG3_FLAG_TAGGED_STATUS) { | ||
3610 | tp->last_tag = sblk->status_tag; | ||
3611 | rmb(); | ||
3612 | } else | ||
3613 | sblk->status &= ~SD_STATUS_UPDATED; | ||
3614 | |||
3615 | netif_rx_complete(tp->dev, napi); | ||
3616 | tg3_restart_ints(tp); | ||
3617 | break; | ||
3618 | } | ||
3605 | } | 3619 | } |
3606 | 3620 | ||
3607 | return work_done; | 3621 | return work_done; |
3622 | |||
3623 | tx_recovery: | ||
3624 | netif_rx_complete(tp->dev, napi); | ||
3625 | schedule_work(&tp->reset_task); | ||
3626 | return 0; | ||
3608 | } | 3627 | } |
3609 | 3628 | ||
3610 | static void tg3_irq_quiesce(struct tg3 *tp) | 3629 | static void tg3_irq_quiesce(struct tg3 *tp) |