From 4fd7ab5949edfdf99be0ceef206c9d0b7f186318 Mon Sep 17 00:00:00 2001 From: Michael Chan Date: Fri, 12 Oct 2007 01:39:50 -0700 Subject: [PATCH 1/2] [TG3]: Refine napi poll loop. Need to read and store sblk->status_tag before checking for more work. The status tag is later written back to the hardware when enabling interrupts to acknowledge how much work has been processed. If the order is reversed, we can end up acknowledging work we haven't processed. When we detect tx error, it is more correct to return the rx work_done so far instead of 0. Signed-off-by: Michael Chan Signed-off-by: David S. Miller --- drivers/net/tg3.c | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/drivers/net/tg3.c b/drivers/net/tg3.c index e795c33b982d..30b1cca8144c 100644 --- a/drivers/net/tg3.c +++ b/drivers/net/tg3.c @@ -3576,7 +3576,7 @@ static int tg3_poll_work(struct tg3 *tp, int work_done, int budget) if (sblk->idx[0].tx_consumer != tp->tx_cons) { tg3_tx(tp); if (unlikely(tp->tg3_flags & TG3_FLAG_TX_RECOVERY_PENDING)) - return 0; + return work_done; } /* run RX thread, within the bounds set by NAPI. @@ -3593,6 +3593,7 @@ static int tg3_poll(struct napi_struct *napi, int budget) { struct tg3 *tp = container_of(napi, struct tg3, napi); int work_done = 0; + struct tg3_hw_status *sblk = tp->hw_status; while (1) { work_done = tg3_poll_work(tp, work_done, budget); @@ -3603,15 +3604,17 @@ static int tg3_poll(struct napi_struct *napi, int budget) if (unlikely(work_done >= budget)) break; + if (tp->tg3_flags & TG3_FLAG_TAGGED_STATUS) { + /* tp->last_tag is used in tg3_restart_ints() below + * to tell the hw how much work has been processed, + * so we must read it before checking for more work. + */ + tp->last_tag = sblk->status_tag; + rmb(); + } else + sblk->status &= ~SD_STATUS_UPDATED; + 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; @@ -3621,9 +3624,10 @@ static int tg3_poll(struct napi_struct *napi, int budget) return work_done; tx_recovery: + /* work_done is guaranteed to be less than budget. */ netif_rx_complete(tp->dev, napi); schedule_work(&tp->reset_task); - return 0; + return work_done; } static void tg3_irq_quiesce(struct tg3 *tp) From 6dee6421581d3484e9a01d403dbf158161942db6 Mon Sep 17 00:00:00 2001 From: Michael Chan Date: Fri, 12 Oct 2007 01:40:38 -0700 Subject: [PATCH 2/2] [BNX2]: Refine napi poll loop. Need to read and store sblk->status_idx before checking for more work. The status idx is later written back to the hardware when enabling interrupts to acknowledge how much work has been processed. If the order is reversed, we can end up acknowledging work we haven't processed. When completing bnx2_poll(), we should always break out of the while loop and return work_done instead of returning 0. Signed-off-by: Michael Chan Signed-off-by: David S. Miller --- drivers/net/bnx2.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/drivers/net/bnx2.c b/drivers/net/bnx2.c index d68accea380b..78ed633ceb82 100644 --- a/drivers/net/bnx2.c +++ b/drivers/net/bnx2.c @@ -2652,10 +2652,10 @@ static int bnx2_poll_work(struct bnx2 *bp, int work_done, int budget) REG_RD(bp, BNX2_HC_COMMAND); } - if (bp->status_blk->status_tx_quick_consumer_index0 != bp->hw_tx_cons) + if (sblk->status_tx_quick_consumer_index0 != bp->hw_tx_cons) bnx2_tx_int(bp); - if (bp->status_blk->status_rx_quick_consumer_index0 != bp->hw_rx_cons) + if (sblk->status_rx_quick_consumer_index0 != bp->hw_rx_cons) work_done += bnx2_rx_int(bp, budget - work_done); return work_done; @@ -2665,6 +2665,7 @@ static int bnx2_poll(struct napi_struct *napi, int budget) { struct bnx2 *bp = container_of(napi, struct bnx2, napi); int work_done = 0; + struct status_block *sblk = bp->status_blk; while (1) { work_done = bnx2_poll_work(bp, work_done, budget); @@ -2672,16 +2673,19 @@ static int bnx2_poll(struct napi_struct *napi, int budget) if (unlikely(work_done >= budget)) break; + /* bp->last_status_idx is used below to tell the hw how + * much work has been processed, so we must read it before + * checking for more work. + */ + bp->last_status_idx = sblk->status_idx; + rmb(); if (likely(!bnx2_has_work(bp))) { - bp->last_status_idx = bp->status_blk->status_idx; - rmb(); - netif_rx_complete(bp->dev, napi); if (likely(bp->flags & USING_MSI_FLAG)) { REG_WR(bp, BNX2_PCICFG_INT_ACK_CMD, BNX2_PCICFG_INT_ACK_CMD_INDEX_VALID | bp->last_status_idx); - return 0; + break; } REG_WR(bp, BNX2_PCICFG_INT_ACK_CMD, BNX2_PCICFG_INT_ACK_CMD_INDEX_VALID |