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 | |
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')
-rw-r--r-- | drivers/net/bnx2.c | 52 | ||||
-rw-r--r-- | drivers/net/sky2.c | 17 | ||||
-rw-r--r-- | drivers/net/tg3.c | 55 |
3 files changed, 82 insertions, 42 deletions
diff --git a/drivers/net/bnx2.c b/drivers/net/bnx2.c index bbfbdafb4ae5..d68accea380b 100644 --- a/drivers/net/bnx2.c +++ b/drivers/net/bnx2.c | |||
@@ -2633,15 +2633,11 @@ bnx2_has_work(struct bnx2 *bp) | |||
2633 | return 0; | 2633 | return 0; |
2634 | } | 2634 | } |
2635 | 2635 | ||
2636 | static int | 2636 | static int bnx2_poll_work(struct bnx2 *bp, int work_done, int budget) |
2637 | bnx2_poll(struct napi_struct *napi, int budget) | ||
2638 | { | 2637 | { |
2639 | struct bnx2 *bp = container_of(napi, struct bnx2, napi); | ||
2640 | struct net_device *dev = bp->dev; | ||
2641 | struct status_block *sblk = bp->status_blk; | 2638 | struct status_block *sblk = bp->status_blk; |
2642 | u32 status_attn_bits = sblk->status_attn_bits; | 2639 | u32 status_attn_bits = sblk->status_attn_bits; |
2643 | u32 status_attn_bits_ack = sblk->status_attn_bits_ack; | 2640 | u32 status_attn_bits_ack = sblk->status_attn_bits_ack; |
2644 | int work_done = 0; | ||
2645 | 2641 | ||
2646 | if ((status_attn_bits & STATUS_ATTN_EVENTS) != | 2642 | if ((status_attn_bits & STATUS_ATTN_EVENTS) != |
2647 | (status_attn_bits_ack & STATUS_ATTN_EVENTS)) { | 2643 | (status_attn_bits_ack & STATUS_ATTN_EVENTS)) { |
@@ -2660,27 +2656,43 @@ bnx2_poll(struct napi_struct *napi, int budget) | |||
2660 | bnx2_tx_int(bp); | 2656 | bnx2_tx_int(bp); |
2661 | 2657 | ||
2662 | if (bp->status_blk->status_rx_quick_consumer_index0 != bp->hw_rx_cons) | 2658 | if (bp->status_blk->status_rx_quick_consumer_index0 != bp->hw_rx_cons) |
2663 | work_done = bnx2_rx_int(bp, budget); | 2659 | work_done += bnx2_rx_int(bp, budget - work_done); |
2664 | 2660 | ||
2665 | bp->last_status_idx = bp->status_blk->status_idx; | 2661 | return work_done; |
2666 | rmb(); | 2662 | } |
2663 | |||
2664 | static int bnx2_poll(struct napi_struct *napi, int budget) | ||
2665 | { | ||
2666 | struct bnx2 *bp = container_of(napi, struct bnx2, napi); | ||
2667 | int work_done = 0; | ||
2668 | |||
2669 | while (1) { | ||
2670 | work_done = bnx2_poll_work(bp, work_done, budget); | ||
2667 | 2671 | ||
2668 | if (!bnx2_has_work(bp)) { | 2672 | if (unlikely(work_done >= budget)) |
2669 | netif_rx_complete(dev, napi); | 2673 | break; |
2670 | if (likely(bp->flags & USING_MSI_FLAG)) { | 2674 | |
2675 | if (likely(!bnx2_has_work(bp))) { | ||
2676 | bp->last_status_idx = bp->status_blk->status_idx; | ||
2677 | rmb(); | ||
2678 | |||
2679 | netif_rx_complete(bp->dev, napi); | ||
2680 | if (likely(bp->flags & USING_MSI_FLAG)) { | ||
2681 | REG_WR(bp, BNX2_PCICFG_INT_ACK_CMD, | ||
2682 | BNX2_PCICFG_INT_ACK_CMD_INDEX_VALID | | ||
2683 | bp->last_status_idx); | ||
2684 | return 0; | ||
2685 | } | ||
2671 | REG_WR(bp, BNX2_PCICFG_INT_ACK_CMD, | 2686 | REG_WR(bp, BNX2_PCICFG_INT_ACK_CMD, |
2672 | BNX2_PCICFG_INT_ACK_CMD_INDEX_VALID | | 2687 | BNX2_PCICFG_INT_ACK_CMD_INDEX_VALID | |
2688 | BNX2_PCICFG_INT_ACK_CMD_MASK_INT | | ||
2673 | bp->last_status_idx); | 2689 | bp->last_status_idx); |
2674 | return 0; | ||
2675 | } | ||
2676 | REG_WR(bp, BNX2_PCICFG_INT_ACK_CMD, | ||
2677 | BNX2_PCICFG_INT_ACK_CMD_INDEX_VALID | | ||
2678 | BNX2_PCICFG_INT_ACK_CMD_MASK_INT | | ||
2679 | bp->last_status_idx); | ||
2680 | 2690 | ||
2681 | REG_WR(bp, BNX2_PCICFG_INT_ACK_CMD, | 2691 | REG_WR(bp, BNX2_PCICFG_INT_ACK_CMD, |
2682 | BNX2_PCICFG_INT_ACK_CMD_INDEX_VALID | | 2692 | BNX2_PCICFG_INT_ACK_CMD_INDEX_VALID | |
2683 | bp->last_status_idx); | 2693 | bp->last_status_idx); |
2694 | break; | ||
2695 | } | ||
2684 | } | 2696 | } |
2685 | 2697 | ||
2686 | return work_done; | 2698 | return work_done; |
diff --git a/drivers/net/sky2.c b/drivers/net/sky2.c index fe0e7560d236..4e569fa0f96c 100644 --- a/drivers/net/sky2.c +++ b/drivers/net/sky2.c | |||
@@ -2606,7 +2606,7 @@ static int sky2_poll(struct napi_struct *napi, int work_limit) | |||
2606 | { | 2606 | { |
2607 | struct sky2_hw *hw = container_of(napi, struct sky2_hw, napi); | 2607 | struct sky2_hw *hw = container_of(napi, struct sky2_hw, napi); |
2608 | u32 status = sky2_read32(hw, B0_Y2_SP_EISR); | 2608 | u32 status = sky2_read32(hw, B0_Y2_SP_EISR); |
2609 | int work_done; | 2609 | int work_done = 0; |
2610 | 2610 | ||
2611 | if (unlikely(status & Y2_IS_ERROR)) | 2611 | if (unlikely(status & Y2_IS_ERROR)) |
2612 | sky2_err_intr(hw, status); | 2612 | sky2_err_intr(hw, status); |
@@ -2617,10 +2617,16 @@ static int sky2_poll(struct napi_struct *napi, int work_limit) | |||
2617 | if (status & Y2_IS_IRQ_PHY2) | 2617 | if (status & Y2_IS_IRQ_PHY2) |
2618 | sky2_phy_intr(hw, 1); | 2618 | sky2_phy_intr(hw, 1); |
2619 | 2619 | ||
2620 | work_done = sky2_status_intr(hw, work_limit); | 2620 | for(;;) { |
2621 | work_done += sky2_status_intr(hw, work_limit); | ||
2622 | |||
2623 | if (work_done >= work_limit) | ||
2624 | break; | ||
2625 | |||
2626 | /* More work? */ | ||
2627 | if (hw->st_idx != sky2_read16(hw, STAT_PUT_IDX)) | ||
2628 | continue; | ||
2621 | 2629 | ||
2622 | /* More work? */ | ||
2623 | if (hw->st_idx == sky2_read16(hw, STAT_PUT_IDX)) { | ||
2624 | /* Bug/Errata workaround? | 2630 | /* Bug/Errata workaround? |
2625 | * Need to kick the TX irq moderation timer. | 2631 | * Need to kick the TX irq moderation timer. |
2626 | */ | 2632 | */ |
@@ -2631,7 +2637,10 @@ static int sky2_poll(struct napi_struct *napi, int work_limit) | |||
2631 | 2637 | ||
2632 | napi_complete(napi); | 2638 | napi_complete(napi); |
2633 | sky2_read32(hw, B0_Y2_SP_LISR); | 2639 | sky2_read32(hw, B0_Y2_SP_LISR); |
2640 | break; | ||
2641 | |||
2634 | } | 2642 | } |
2643 | |||
2635 | return work_done; | 2644 | return work_done; |
2636 | } | 2645 | } |
2637 | 2646 | ||
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) |