diff options
author | stephen hemminger <shemminger@vyatta.com> | 2010-12-31 10:34:27 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2011-01-01 17:02:24 -0500 |
commit | 0885a30b699a2c96d892b61cc48e8ba68fe87bfc (patch) | |
tree | 870ab51f527bc7369190075682e739b274f01803 | |
parent | a016892cd6eb8d3dd9769021b088917ac7371abd (diff) |
sky2: implement 64 bit stats
This implements 64 bit statistics support and fixes races when reading
counter values. The PHY counters can only be accessed 16 bits at a time,
so they are subject to carry races.
NB:
* TX/RX counters are maintained in software because the the hardware packet count
is only a 32 bit value.
* Error counters are really only 32 bit.
* Old 32 bit counter fields in dev->stats still used for some
software counters
Signed-off-by: Stephen Hemminger <shemminger@vyatta.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r-- | drivers/net/sky2.c | 103 | ||||
-rw-r--r-- | drivers/net/sky2.h | 42 |
2 files changed, 109 insertions, 36 deletions
diff --git a/drivers/net/sky2.c b/drivers/net/sky2.c index 0d4a236c3bb3..39996bf3b247 100644 --- a/drivers/net/sky2.c +++ b/drivers/net/sky2.c | |||
@@ -1917,8 +1917,10 @@ static void sky2_tx_complete(struct sky2_port *sky2, u16 done) | |||
1917 | netif_printk(sky2, tx_done, KERN_DEBUG, dev, | 1917 | netif_printk(sky2, tx_done, KERN_DEBUG, dev, |
1918 | "tx done %u\n", idx); | 1918 | "tx done %u\n", idx); |
1919 | 1919 | ||
1920 | dev->stats.tx_packets++; | 1920 | u64_stats_update_begin(&sky2->tx_stats.syncp); |
1921 | dev->stats.tx_bytes += skb->len; | 1921 | ++sky2->tx_stats.packets; |
1922 | sky2->tx_stats.bytes += skb->len; | ||
1923 | u64_stats_update_end(&sky2->tx_stats.syncp); | ||
1922 | 1924 | ||
1923 | re->skb = NULL; | 1925 | re->skb = NULL; |
1924 | dev_kfree_skb_any(skb); | 1926 | dev_kfree_skb_any(skb); |
@@ -2460,7 +2462,7 @@ static struct sk_buff *sky2_receive(struct net_device *dev, | |||
2460 | 2462 | ||
2461 | /* if length reported by DMA does not match PHY, packet was truncated */ | 2463 | /* if length reported by DMA does not match PHY, packet was truncated */ |
2462 | if (length != count) | 2464 | if (length != count) |
2463 | goto len_error; | 2465 | goto error; |
2464 | 2466 | ||
2465 | okay: | 2467 | okay: |
2466 | if (length < copybreak) | 2468 | if (length < copybreak) |
@@ -2475,34 +2477,13 @@ resubmit: | |||
2475 | 2477 | ||
2476 | return skb; | 2478 | return skb; |
2477 | 2479 | ||
2478 | len_error: | ||
2479 | /* Truncation of overlength packets | ||
2480 | causes PHY length to not match MAC length */ | ||
2481 | ++dev->stats.rx_length_errors; | ||
2482 | if (net_ratelimit()) | ||
2483 | netif_info(sky2, rx_err, dev, | ||
2484 | "rx length error: status %#x length %d\n", | ||
2485 | status, length); | ||
2486 | goto resubmit; | ||
2487 | |||
2488 | error: | 2480 | error: |
2489 | ++dev->stats.rx_errors; | 2481 | ++dev->stats.rx_errors; |
2490 | if (status & GMR_FS_RX_FF_OV) { | ||
2491 | dev->stats.rx_over_errors++; | ||
2492 | goto resubmit; | ||
2493 | } | ||
2494 | 2482 | ||
2495 | if (net_ratelimit()) | 2483 | if (net_ratelimit()) |
2496 | netif_info(sky2, rx_err, dev, | 2484 | netif_info(sky2, rx_err, dev, |
2497 | "rx error, status 0x%x length %d\n", status, length); | 2485 | "rx error, status 0x%x length %d\n", status, length); |
2498 | 2486 | ||
2499 | if (status & (GMR_FS_LONG_ERR | GMR_FS_UN_SIZE)) | ||
2500 | dev->stats.rx_length_errors++; | ||
2501 | if (status & GMR_FS_FRAGMENT) | ||
2502 | dev->stats.rx_frame_errors++; | ||
2503 | if (status & GMR_FS_CRC_ERR) | ||
2504 | dev->stats.rx_crc_errors++; | ||
2505 | |||
2506 | goto resubmit; | 2487 | goto resubmit; |
2507 | } | 2488 | } |
2508 | 2489 | ||
@@ -2543,14 +2524,19 @@ static inline void sky2_skb_rx(const struct sky2_port *sky2, | |||
2543 | static inline void sky2_rx_done(struct sky2_hw *hw, unsigned port, | 2524 | static inline void sky2_rx_done(struct sky2_hw *hw, unsigned port, |
2544 | unsigned packets, unsigned bytes) | 2525 | unsigned packets, unsigned bytes) |
2545 | { | 2526 | { |
2546 | if (packets) { | 2527 | struct net_device *dev = hw->dev[port]; |
2547 | struct net_device *dev = hw->dev[port]; | 2528 | struct sky2_port *sky2 = netdev_priv(dev); |
2548 | 2529 | ||
2549 | dev->stats.rx_packets += packets; | 2530 | if (packets == 0) |
2550 | dev->stats.rx_bytes += bytes; | 2531 | return; |
2551 | dev->last_rx = jiffies; | 2532 | |
2552 | sky2_rx_update(netdev_priv(dev), rxqaddr[port]); | 2533 | u64_stats_update_begin(&sky2->rx_stats.syncp); |
2553 | } | 2534 | sky2->rx_stats.packets += packets; |
2535 | sky2->rx_stats.bytes += bytes; | ||
2536 | u64_stats_update_end(&sky2->rx_stats.syncp); | ||
2537 | |||
2538 | dev->last_rx = jiffies; | ||
2539 | sky2_rx_update(netdev_priv(dev), rxqaddr[port]); | ||
2554 | } | 2540 | } |
2555 | 2541 | ||
2556 | static void sky2_rx_checksum(struct sky2_port *sky2, u32 status) | 2542 | static void sky2_rx_checksum(struct sky2_port *sky2, u32 status) |
@@ -3626,13 +3612,11 @@ static void sky2_phy_stats(struct sky2_port *sky2, u64 * data, unsigned count) | |||
3626 | unsigned port = sky2->port; | 3612 | unsigned port = sky2->port; |
3627 | int i; | 3613 | int i; |
3628 | 3614 | ||
3629 | data[0] = (u64) gma_read32(hw, port, GM_TXO_OK_HI) << 32 | 3615 | data[0] = get_stats64(hw, port, GM_TXO_OK_LO); |
3630 | | (u64) gma_read32(hw, port, GM_TXO_OK_LO); | 3616 | data[1] = get_stats64(hw, port, GM_RXO_OK_LO); |
3631 | data[1] = (u64) gma_read32(hw, port, GM_RXO_OK_HI) << 32 | ||
3632 | | (u64) gma_read32(hw, port, GM_RXO_OK_LO); | ||
3633 | 3617 | ||
3634 | for (i = 2; i < count; i++) | 3618 | for (i = 2; i < count; i++) |
3635 | data[i] = (u64) gma_read32(hw, port, sky2_stats[i].offset); | 3619 | data[i] = get_stats32(hw, port, sky2_stats[i].offset); |
3636 | } | 3620 | } |
3637 | 3621 | ||
3638 | static void sky2_set_msglevel(struct net_device *netdev, u32 value) | 3622 | static void sky2_set_msglevel(struct net_device *netdev, u32 value) |
@@ -3750,6 +3734,51 @@ static void sky2_set_multicast(struct net_device *dev) | |||
3750 | gma_write16(hw, port, GM_RX_CTRL, reg); | 3734 | gma_write16(hw, port, GM_RX_CTRL, reg); |
3751 | } | 3735 | } |
3752 | 3736 | ||
3737 | static struct rtnl_link_stats64 *sky2_get_stats(struct net_device *dev, | ||
3738 | struct rtnl_link_stats64 *stats) | ||
3739 | { | ||
3740 | struct sky2_port *sky2 = netdev_priv(dev); | ||
3741 | struct sky2_hw *hw = sky2->hw; | ||
3742 | unsigned port = sky2->port; | ||
3743 | unsigned int start; | ||
3744 | u64 _bytes, _packets; | ||
3745 | |||
3746 | do { | ||
3747 | start = u64_stats_fetch_begin_bh(&sky2->rx_stats.syncp); | ||
3748 | _bytes = sky2->rx_stats.bytes; | ||
3749 | _packets = sky2->rx_stats.packets; | ||
3750 | } while (u64_stats_fetch_retry_bh(&sky2->rx_stats.syncp, start)); | ||
3751 | |||
3752 | stats->rx_packets = _packets; | ||
3753 | stats->rx_bytes = _bytes; | ||
3754 | |||
3755 | do { | ||
3756 | start = u64_stats_fetch_begin_bh(&sky2->tx_stats.syncp); | ||
3757 | _bytes = sky2->tx_stats.bytes; | ||
3758 | _packets = sky2->tx_stats.packets; | ||
3759 | } while (u64_stats_fetch_retry_bh(&sky2->tx_stats.syncp, start)); | ||
3760 | |||
3761 | stats->tx_packets = _packets; | ||
3762 | stats->tx_bytes = _bytes; | ||
3763 | |||
3764 | stats->multicast = get_stats32(hw, port, GM_RXF_MC_OK) | ||
3765 | + get_stats32(hw, port, GM_RXF_BC_OK); | ||
3766 | |||
3767 | stats->collisions = get_stats32(hw, port, GM_TXF_COL); | ||
3768 | |||
3769 | stats->rx_length_errors = get_stats32(hw, port, GM_RXF_LNG_ERR); | ||
3770 | stats->rx_crc_errors = get_stats32(hw, port, GM_RXF_FCS_ERR); | ||
3771 | stats->rx_frame_errors = get_stats32(hw, port, GM_RXF_SHT) | ||
3772 | + get_stats32(hw, port, GM_RXE_FRAG); | ||
3773 | stats->rx_over_errors = get_stats32(hw, port, GM_RXE_FIFO_OV); | ||
3774 | |||
3775 | stats->rx_dropped = dev->stats.rx_dropped; | ||
3776 | stats->rx_fifo_errors = dev->stats.rx_fifo_errors; | ||
3777 | stats->tx_fifo_errors = dev->stats.tx_fifo_errors; | ||
3778 | |||
3779 | return stats; | ||
3780 | } | ||
3781 | |||
3753 | /* Can have one global because blinking is controlled by | 3782 | /* Can have one global because blinking is controlled by |
3754 | * ethtool and that is always under RTNL mutex | 3783 | * ethtool and that is always under RTNL mutex |
3755 | */ | 3784 | */ |
@@ -4524,6 +4553,7 @@ static const struct net_device_ops sky2_netdev_ops[2] = { | |||
4524 | .ndo_set_multicast_list = sky2_set_multicast, | 4553 | .ndo_set_multicast_list = sky2_set_multicast, |
4525 | .ndo_change_mtu = sky2_change_mtu, | 4554 | .ndo_change_mtu = sky2_change_mtu, |
4526 | .ndo_tx_timeout = sky2_tx_timeout, | 4555 | .ndo_tx_timeout = sky2_tx_timeout, |
4556 | .ndo_get_stats64 = sky2_get_stats, | ||
4527 | #ifdef SKY2_VLAN_TAG_USED | 4557 | #ifdef SKY2_VLAN_TAG_USED |
4528 | .ndo_vlan_rx_register = sky2_vlan_rx_register, | 4558 | .ndo_vlan_rx_register = sky2_vlan_rx_register, |
4529 | #endif | 4559 | #endif |
@@ -4541,6 +4571,7 @@ static const struct net_device_ops sky2_netdev_ops[2] = { | |||
4541 | .ndo_set_multicast_list = sky2_set_multicast, | 4571 | .ndo_set_multicast_list = sky2_set_multicast, |
4542 | .ndo_change_mtu = sky2_change_mtu, | 4572 | .ndo_change_mtu = sky2_change_mtu, |
4543 | .ndo_tx_timeout = sky2_tx_timeout, | 4573 | .ndo_tx_timeout = sky2_tx_timeout, |
4574 | .ndo_get_stats64 = sky2_get_stats, | ||
4544 | #ifdef SKY2_VLAN_TAG_USED | 4575 | #ifdef SKY2_VLAN_TAG_USED |
4545 | .ndo_vlan_rx_register = sky2_vlan_rx_register, | 4576 | .ndo_vlan_rx_register = sky2_vlan_rx_register, |
4546 | #endif | 4577 | #endif |
diff --git a/drivers/net/sky2.h b/drivers/net/sky2.h index 61891a6cacc2..80bdc404f1ea 100644 --- a/drivers/net/sky2.h +++ b/drivers/net/sky2.h | |||
@@ -2200,6 +2200,12 @@ enum flow_control { | |||
2200 | FC_BOTH = 3, | 2200 | FC_BOTH = 3, |
2201 | }; | 2201 | }; |
2202 | 2202 | ||
2203 | struct sky2_stats { | ||
2204 | struct u64_stats_sync syncp; | ||
2205 | u64 packets; | ||
2206 | u64 bytes; | ||
2207 | }; | ||
2208 | |||
2203 | struct sky2_port { | 2209 | struct sky2_port { |
2204 | struct sky2_hw *hw; | 2210 | struct sky2_hw *hw; |
2205 | struct net_device *netdev; | 2211 | struct net_device *netdev; |
@@ -2209,6 +2215,8 @@ struct sky2_port { | |||
2209 | 2215 | ||
2210 | struct tx_ring_info *tx_ring; | 2216 | struct tx_ring_info *tx_ring; |
2211 | struct sky2_tx_le *tx_le; | 2217 | struct sky2_tx_le *tx_le; |
2218 | struct sky2_stats tx_stats; | ||
2219 | |||
2212 | u16 tx_ring_size; | 2220 | u16 tx_ring_size; |
2213 | u16 tx_cons; /* next le to check */ | 2221 | u16 tx_cons; /* next le to check */ |
2214 | u16 tx_prod; /* next le to use */ | 2222 | u16 tx_prod; /* next le to use */ |
@@ -2221,6 +2229,7 @@ struct sky2_port { | |||
2221 | 2229 | ||
2222 | struct rx_ring_info *rx_ring ____cacheline_aligned_in_smp; | 2230 | struct rx_ring_info *rx_ring ____cacheline_aligned_in_smp; |
2223 | struct sky2_rx_le *rx_le; | 2231 | struct sky2_rx_le *rx_le; |
2232 | struct sky2_stats rx_stats; | ||
2224 | 2233 | ||
2225 | u16 rx_next; /* next re to check */ | 2234 | u16 rx_next; /* next re to check */ |
2226 | u16 rx_put; /* next le index to use */ | 2235 | u16 rx_put; /* next le index to use */ |
@@ -2346,6 +2355,39 @@ static inline u32 gma_read32(struct sky2_hw *hw, unsigned port, unsigned reg) | |||
2346 | | (u32) sky2_read16(hw, base+4) << 16; | 2355 | | (u32) sky2_read16(hw, base+4) << 16; |
2347 | } | 2356 | } |
2348 | 2357 | ||
2358 | static inline u64 gma_read64(struct sky2_hw *hw, unsigned port, unsigned reg) | ||
2359 | { | ||
2360 | unsigned base = SK_GMAC_REG(port, reg); | ||
2361 | |||
2362 | return (u64) sky2_read16(hw, base) | ||
2363 | | (u64) sky2_read16(hw, base+4) << 16 | ||
2364 | | (u64) sky2_read16(hw, base+8) << 32 | ||
2365 | | (u64) sky2_read16(hw, base+12) << 48; | ||
2366 | } | ||
2367 | |||
2368 | /* There is no way to atomically read32 bit values from PHY, so retry */ | ||
2369 | static inline u32 get_stats32(struct sky2_hw *hw, unsigned port, unsigned reg) | ||
2370 | { | ||
2371 | u32 val; | ||
2372 | |||
2373 | do { | ||
2374 | val = gma_read32(hw, port, reg); | ||
2375 | } while (gma_read32(hw, port, reg) != val); | ||
2376 | |||
2377 | return val; | ||
2378 | } | ||
2379 | |||
2380 | static inline u64 get_stats64(struct sky2_hw *hw, unsigned port, unsigned reg) | ||
2381 | { | ||
2382 | u64 val; | ||
2383 | |||
2384 | do { | ||
2385 | val = gma_read64(hw, port, reg); | ||
2386 | } while (gma_read64(hw, port, reg) != val); | ||
2387 | |||
2388 | return val; | ||
2389 | } | ||
2390 | |||
2349 | static inline void gma_write16(const struct sky2_hw *hw, unsigned port, int r, u16 v) | 2391 | static inline void gma_write16(const struct sky2_hw *hw, unsigned port, int r, u16 v) |
2350 | { | 2392 | { |
2351 | sky2_write16(hw, SK_GMAC_REG(port,r), v); | 2393 | sky2_write16(hw, SK_GMAC_REG(port,r), v); |