diff options
author | Stephen Hemminger <shemminger@linux-foundation.org> | 2007-09-19 18:36:46 -0400 |
---|---|---|
committer | Jeff Garzik <jeff@garzik.org> | 2007-09-20 15:23:00 -0400 |
commit | 75e806838a3327d4ca9030e588d34de11b04f341 (patch) | |
tree | 11dff9f4326b300ff80cbc4f627f797a06ad8c52 /drivers/net | |
parent | 05745c4ab1c58fbb6ab8e8d3a40e0e395d7e2b0e (diff) |
sky2: receive FIFO checking
A driver writer from another operating system hinted that
the versions of Yukon 2 chip with rambuffer (EC and XL) have
a hardware bug that if the FIFO ever gets completely full it
will hang. Sounds like a classic ring full vs ring empty wrap around
bug.
As a workaround, use the existing watchdog timer to check for
ring full lockup.
Signed-off-by: Stephen Hemminger <shemminger@linux-foundation.org>
Signed-off-by: Jeff Garzik <jeff@garzik.org>
Diffstat (limited to 'drivers/net')
-rw-r--r-- | drivers/net/sky2.c | 74 | ||||
-rw-r--r-- | drivers/net/sky2.h | 9 |
2 files changed, 66 insertions, 17 deletions
diff --git a/drivers/net/sky2.c b/drivers/net/sky2.c index d0e5875628e9..2831f44e6270 100644 --- a/drivers/net/sky2.c +++ b/drivers/net/sky2.c | |||
@@ -1648,9 +1648,6 @@ static int sky2_down(struct net_device *dev) | |||
1648 | if (netif_msg_ifdown(sky2)) | 1648 | if (netif_msg_ifdown(sky2)) |
1649 | printk(KERN_INFO PFX "%s: disabling interface\n", dev->name); | 1649 | printk(KERN_INFO PFX "%s: disabling interface\n", dev->name); |
1650 | 1650 | ||
1651 | if (netif_carrier_ok(dev) && --hw->active == 0) | ||
1652 | del_timer(&hw->watchdog_timer); | ||
1653 | |||
1654 | /* Stop more packets from being queued */ | 1651 | /* Stop more packets from being queued */ |
1655 | netif_stop_queue(dev); | 1652 | netif_stop_queue(dev); |
1656 | 1653 | ||
@@ -1775,9 +1772,7 @@ static void sky2_link_up(struct sky2_port *sky2) | |||
1775 | 1772 | ||
1776 | netif_carrier_on(sky2->netdev); | 1773 | netif_carrier_on(sky2->netdev); |
1777 | 1774 | ||
1778 | if (hw->active++ == 0) | 1775 | mod_timer(&hw->watchdog_timer, jiffies + 1); |
1779 | mod_timer(&hw->watchdog_timer, jiffies + 1); | ||
1780 | |||
1781 | 1776 | ||
1782 | /* Turn on link LED */ | 1777 | /* Turn on link LED */ |
1783 | sky2_write8(hw, SK_REG(port, LNK_LED_REG), | 1778 | sky2_write8(hw, SK_REG(port, LNK_LED_REG), |
@@ -1828,11 +1823,6 @@ static void sky2_link_down(struct sky2_port *sky2) | |||
1828 | 1823 | ||
1829 | netif_carrier_off(sky2->netdev); | 1824 | netif_carrier_off(sky2->netdev); |
1830 | 1825 | ||
1831 | /* Stop watchdog if both ports are not active */ | ||
1832 | if (--hw->active == 0) | ||
1833 | del_timer(&hw->watchdog_timer); | ||
1834 | |||
1835 | |||
1836 | /* Turn on link LED */ | 1826 | /* Turn on link LED */ |
1837 | sky2_write8(hw, SK_REG(port, LNK_LED_REG), LINKLED_OFF); | 1827 | sky2_write8(hw, SK_REG(port, LNK_LED_REG), LINKLED_OFF); |
1838 | 1828 | ||
@@ -2484,20 +2474,72 @@ static void sky2_le_error(struct sky2_hw *hw, unsigned port, | |||
2484 | sky2_write32(hw, Q_ADDR(q, Q_CSR), BMU_CLR_IRQ_CHK); | 2474 | sky2_write32(hw, Q_ADDR(q, Q_CSR), BMU_CLR_IRQ_CHK); |
2485 | } | 2475 | } |
2486 | 2476 | ||
2487 | /* Check for lost IRQ once a second */ | 2477 | static int sky2_rx_hung(struct net_device *dev) |
2478 | { | ||
2479 | struct sky2_port *sky2 = netdev_priv(dev); | ||
2480 | struct sky2_hw *hw = sky2->hw; | ||
2481 | unsigned port = sky2->port; | ||
2482 | unsigned rxq = rxqaddr[port]; | ||
2483 | u32 mac_rp = sky2_read32(hw, SK_REG(port, RX_GMF_RP)); | ||
2484 | u8 mac_lev = sky2_read8(hw, SK_REG(port, RX_GMF_RLEV)); | ||
2485 | u8 fifo_rp = sky2_read8(hw, Q_ADDR(rxq, Q_RP)); | ||
2486 | u8 fifo_lev = sky2_read8(hw, Q_ADDR(rxq, Q_RL)); | ||
2487 | |||
2488 | /* If idle and MAC or PCI is stuck */ | ||
2489 | if (sky2->check.last == dev->last_rx && | ||
2490 | ((mac_rp == sky2->check.mac_rp && | ||
2491 | mac_lev != 0 && mac_lev >= sky2->check.mac_lev) || | ||
2492 | /* Check if the PCI RX hang */ | ||
2493 | (fifo_rp == sky2->check.fifo_rp && | ||
2494 | fifo_lev != 0 && fifo_lev >= sky2->check.fifo_lev))) { | ||
2495 | printk(KERN_DEBUG PFX "%s: hung mac %d:%d fifo %d (%d:%d)\n", | ||
2496 | dev->name, mac_lev, mac_rp, fifo_lev, fifo_rp, | ||
2497 | sky2_read8(hw, Q_ADDR(rxq, Q_WP))); | ||
2498 | return 1; | ||
2499 | } else { | ||
2500 | sky2->check.last = dev->last_rx; | ||
2501 | sky2->check.mac_rp = mac_rp; | ||
2502 | sky2->check.mac_lev = mac_lev; | ||
2503 | sky2->check.fifo_rp = fifo_rp; | ||
2504 | sky2->check.fifo_lev = fifo_lev; | ||
2505 | return 0; | ||
2506 | } | ||
2507 | } | ||
2508 | |||
2488 | static void sky2_watchdog(unsigned long arg) | 2509 | static void sky2_watchdog(unsigned long arg) |
2489 | { | 2510 | { |
2490 | struct sky2_hw *hw = (struct sky2_hw *) arg; | 2511 | struct sky2_hw *hw = (struct sky2_hw *) arg; |
2512 | struct net_device *dev; | ||
2491 | 2513 | ||
2514 | /* Check for lost IRQ once a second */ | ||
2492 | if (sky2_read32(hw, B0_ISRC)) { | 2515 | if (sky2_read32(hw, B0_ISRC)) { |
2493 | struct net_device *dev = hw->dev[0]; | 2516 | dev = hw->dev[0]; |
2494 | |||
2495 | if (__netif_rx_schedule_prep(dev)) | 2517 | if (__netif_rx_schedule_prep(dev)) |
2496 | __netif_rx_schedule(dev); | 2518 | __netif_rx_schedule(dev); |
2519 | } else { | ||
2520 | int i, active = 0; | ||
2521 | |||
2522 | for (i = 0; i < hw->ports; i++) { | ||
2523 | dev = hw->dev[i]; | ||
2524 | if (!netif_running(dev)) | ||
2525 | continue; | ||
2526 | ++active; | ||
2527 | |||
2528 | /* For chips with Rx FIFO, check if stuck */ | ||
2529 | if ((hw->flags & SKY2_HW_RAMBUFFER) && | ||
2530 | sky2_rx_hung(dev)) { | ||
2531 | pr_info(PFX "%s: receiver hang detected\n", | ||
2532 | dev->name); | ||
2533 | schedule_work(&hw->restart_work); | ||
2534 | return; | ||
2535 | } | ||
2536 | } | ||
2537 | |||
2538 | if (active == 0) | ||
2539 | return; | ||
2497 | } | 2540 | } |
2498 | 2541 | ||
2499 | if (hw->active > 0) | 2542 | mod_timer(&hw->watchdog_timer, round_jiffies(jiffies + HZ)); |
2500 | mod_timer(&hw->watchdog_timer, round_jiffies(jiffies + HZ)); | ||
2501 | } | 2543 | } |
2502 | 2544 | ||
2503 | /* Hardware/software error handling */ | 2545 | /* Hardware/software error handling */ |
diff --git a/drivers/net/sky2.h b/drivers/net/sky2.h index a05b30b68fa1..69cd98400fe6 100644 --- a/drivers/net/sky2.h +++ b/drivers/net/sky2.h | |||
@@ -2027,6 +2027,14 @@ struct sky2_port { | |||
2027 | u16 rx_tag; | 2027 | u16 rx_tag; |
2028 | struct vlan_group *vlgrp; | 2028 | struct vlan_group *vlgrp; |
2029 | #endif | 2029 | #endif |
2030 | struct { | ||
2031 | unsigned long last; | ||
2032 | u32 mac_rp; | ||
2033 | u8 mac_lev; | ||
2034 | u8 fifo_rp; | ||
2035 | u8 fifo_lev; | ||
2036 | } check; | ||
2037 | |||
2030 | 2038 | ||
2031 | dma_addr_t rx_le_map; | 2039 | dma_addr_t rx_le_map; |
2032 | dma_addr_t tx_le_map; | 2040 | dma_addr_t tx_le_map; |
@@ -2064,7 +2072,6 @@ struct sky2_hw { | |||
2064 | u8 chip_rev; | 2072 | u8 chip_rev; |
2065 | u8 pmd_type; | 2073 | u8 pmd_type; |
2066 | u8 ports; | 2074 | u8 ports; |
2067 | u8 active; | ||
2068 | 2075 | ||
2069 | struct sky2_status_le *st_le; | 2076 | struct sky2_status_le *st_le; |
2070 | u32 st_idx; | 2077 | u32 st_idx; |