From 6f535763165331bb91277d7519b507fed22034e5 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Thu, 11 Oct 2007 18:08:29 -0700 Subject: [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 --- drivers/net/tg3.c | 55 +++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 37 insertions(+), 18 deletions(-) (limited to 'drivers/net/tg3.c') 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) -- cgit v1.2.3