Commit 0885a30b authored by stephen hemminger's avatar stephen hemminger Committed by David S. Miller

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: default avatarStephen Hemminger <shemminger@vyatta.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent a016892c
...@@ -1917,8 +1917,10 @@ static void sky2_tx_complete(struct sky2_port *sky2, u16 done) ...@@ -1917,8 +1917,10 @@ static void sky2_tx_complete(struct sky2_port *sky2, u16 done)
netif_printk(sky2, tx_done, KERN_DEBUG, dev, netif_printk(sky2, tx_done, KERN_DEBUG, dev,
"tx done %u\n", idx); "tx done %u\n", idx);
dev->stats.tx_packets++; u64_stats_update_begin(&sky2->tx_stats.syncp);
dev->stats.tx_bytes += skb->len; ++sky2->tx_stats.packets;
sky2->tx_stats.bytes += skb->len;
u64_stats_update_end(&sky2->tx_stats.syncp);
re->skb = NULL; re->skb = NULL;
dev_kfree_skb_any(skb); dev_kfree_skb_any(skb);
...@@ -2460,7 +2462,7 @@ static struct sk_buff *sky2_receive(struct net_device *dev, ...@@ -2460,7 +2462,7 @@ static struct sk_buff *sky2_receive(struct net_device *dev,
/* if length reported by DMA does not match PHY, packet was truncated */ /* if length reported by DMA does not match PHY, packet was truncated */
if (length != count) if (length != count)
goto len_error; goto error;
okay: okay:
if (length < copybreak) if (length < copybreak)
...@@ -2475,34 +2477,13 @@ static struct sk_buff *sky2_receive(struct net_device *dev, ...@@ -2475,34 +2477,13 @@ static struct sk_buff *sky2_receive(struct net_device *dev,
return skb; return skb;
len_error:
/* Truncation of overlength packets
causes PHY length to not match MAC length */
++dev->stats.rx_length_errors;
if (net_ratelimit())
netif_info(sky2, rx_err, dev,
"rx length error: status %#x length %d\n",
status, length);
goto resubmit;
error: error:
++dev->stats.rx_errors; ++dev->stats.rx_errors;
if (status & GMR_FS_RX_FF_OV) {
dev->stats.rx_over_errors++;
goto resubmit;
}
if (net_ratelimit()) if (net_ratelimit())
netif_info(sky2, rx_err, dev, netif_info(sky2, rx_err, dev,
"rx error, status 0x%x length %d\n", status, length); "rx error, status 0x%x length %d\n", status, length);
if (status & (GMR_FS_LONG_ERR | GMR_FS_UN_SIZE))
dev->stats.rx_length_errors++;
if (status & GMR_FS_FRAGMENT)
dev->stats.rx_frame_errors++;
if (status & GMR_FS_CRC_ERR)
dev->stats.rx_crc_errors++;
goto resubmit; goto resubmit;
} }
...@@ -2543,14 +2524,19 @@ static inline void sky2_skb_rx(const struct sky2_port *sky2, ...@@ -2543,14 +2524,19 @@ static inline void sky2_skb_rx(const struct sky2_port *sky2,
static inline void sky2_rx_done(struct sky2_hw *hw, unsigned port, static inline void sky2_rx_done(struct sky2_hw *hw, unsigned port,
unsigned packets, unsigned bytes) unsigned packets, unsigned bytes)
{ {
if (packets) {
struct net_device *dev = hw->dev[port]; struct net_device *dev = hw->dev[port];
struct sky2_port *sky2 = netdev_priv(dev);
if (packets == 0)
return;
u64_stats_update_begin(&sky2->rx_stats.syncp);
sky2->rx_stats.packets += packets;
sky2->rx_stats.bytes += bytes;
u64_stats_update_end(&sky2->rx_stats.syncp);
dev->stats.rx_packets += packets;
dev->stats.rx_bytes += bytes;
dev->last_rx = jiffies; dev->last_rx = jiffies;
sky2_rx_update(netdev_priv(dev), rxqaddr[port]); sky2_rx_update(netdev_priv(dev), rxqaddr[port]);
}
} }
static void sky2_rx_checksum(struct sky2_port *sky2, u32 status) 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,13 +3612,11 @@ static void sky2_phy_stats(struct sky2_port *sky2, u64 * data, unsigned count)
unsigned port = sky2->port; unsigned port = sky2->port;
int i; int i;
data[0] = (u64) gma_read32(hw, port, GM_TXO_OK_HI) << 32 data[0] = get_stats64(hw, port, GM_TXO_OK_LO);
| (u64) gma_read32(hw, port, GM_TXO_OK_LO); data[1] = get_stats64(hw, port, GM_RXO_OK_LO);
data[1] = (u64) gma_read32(hw, port, GM_RXO_OK_HI) << 32
| (u64) gma_read32(hw, port, GM_RXO_OK_LO);
for (i = 2; i < count; i++) for (i = 2; i < count; i++)
data[i] = (u64) gma_read32(hw, port, sky2_stats[i].offset); data[i] = get_stats32(hw, port, sky2_stats[i].offset);
} }
static void sky2_set_msglevel(struct net_device *netdev, u32 value) 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,6 +3734,51 @@ static void sky2_set_multicast(struct net_device *dev)
gma_write16(hw, port, GM_RX_CTRL, reg); gma_write16(hw, port, GM_RX_CTRL, reg);
} }
static struct rtnl_link_stats64 *sky2_get_stats(struct net_device *dev,
struct rtnl_link_stats64 *stats)
{
struct sky2_port *sky2 = netdev_priv(dev);
struct sky2_hw *hw = sky2->hw;
unsigned port = sky2->port;
unsigned int start;
u64 _bytes, _packets;
do {
start = u64_stats_fetch_begin_bh(&sky2->rx_stats.syncp);
_bytes = sky2->rx_stats.bytes;
_packets = sky2->rx_stats.packets;
} while (u64_stats_fetch_retry_bh(&sky2->rx_stats.syncp, start));
stats->rx_packets = _packets;
stats->rx_bytes = _bytes;
do {
start = u64_stats_fetch_begin_bh(&sky2->tx_stats.syncp);
_bytes = sky2->tx_stats.bytes;
_packets = sky2->tx_stats.packets;
} while (u64_stats_fetch_retry_bh(&sky2->tx_stats.syncp, start));
stats->tx_packets = _packets;
stats->tx_bytes = _bytes;
stats->multicast = get_stats32(hw, port, GM_RXF_MC_OK)
+ get_stats32(hw, port, GM_RXF_BC_OK);
stats->collisions = get_stats32(hw, port, GM_TXF_COL);
stats->rx_length_errors = get_stats32(hw, port, GM_RXF_LNG_ERR);
stats->rx_crc_errors = get_stats32(hw, port, GM_RXF_FCS_ERR);
stats->rx_frame_errors = get_stats32(hw, port, GM_RXF_SHT)
+ get_stats32(hw, port, GM_RXE_FRAG);
stats->rx_over_errors = get_stats32(hw, port, GM_RXE_FIFO_OV);
stats->rx_dropped = dev->stats.rx_dropped;
stats->rx_fifo_errors = dev->stats.rx_fifo_errors;
stats->tx_fifo_errors = dev->stats.tx_fifo_errors;
return stats;
}
/* Can have one global because blinking is controlled by /* Can have one global because blinking is controlled by
* ethtool and that is always under RTNL mutex * ethtool and that is always under RTNL mutex
*/ */
...@@ -4524,6 +4553,7 @@ static const struct net_device_ops sky2_netdev_ops[2] = { ...@@ -4524,6 +4553,7 @@ static const struct net_device_ops sky2_netdev_ops[2] = {
.ndo_set_multicast_list = sky2_set_multicast, .ndo_set_multicast_list = sky2_set_multicast,
.ndo_change_mtu = sky2_change_mtu, .ndo_change_mtu = sky2_change_mtu,
.ndo_tx_timeout = sky2_tx_timeout, .ndo_tx_timeout = sky2_tx_timeout,
.ndo_get_stats64 = sky2_get_stats,
#ifdef SKY2_VLAN_TAG_USED #ifdef SKY2_VLAN_TAG_USED
.ndo_vlan_rx_register = sky2_vlan_rx_register, .ndo_vlan_rx_register = sky2_vlan_rx_register,
#endif #endif
...@@ -4541,6 +4571,7 @@ static const struct net_device_ops sky2_netdev_ops[2] = { ...@@ -4541,6 +4571,7 @@ static const struct net_device_ops sky2_netdev_ops[2] = {
.ndo_set_multicast_list = sky2_set_multicast, .ndo_set_multicast_list = sky2_set_multicast,
.ndo_change_mtu = sky2_change_mtu, .ndo_change_mtu = sky2_change_mtu,
.ndo_tx_timeout = sky2_tx_timeout, .ndo_tx_timeout = sky2_tx_timeout,
.ndo_get_stats64 = sky2_get_stats,
#ifdef SKY2_VLAN_TAG_USED #ifdef SKY2_VLAN_TAG_USED
.ndo_vlan_rx_register = sky2_vlan_rx_register, .ndo_vlan_rx_register = sky2_vlan_rx_register,
#endif #endif
......
...@@ -2200,6 +2200,12 @@ enum flow_control { ...@@ -2200,6 +2200,12 @@ enum flow_control {
FC_BOTH = 3, FC_BOTH = 3,
}; };
struct sky2_stats {
struct u64_stats_sync syncp;
u64 packets;
u64 bytes;
};
struct sky2_port { struct sky2_port {
struct sky2_hw *hw; struct sky2_hw *hw;
struct net_device *netdev; struct net_device *netdev;
...@@ -2209,6 +2215,8 @@ struct sky2_port { ...@@ -2209,6 +2215,8 @@ struct sky2_port {
struct tx_ring_info *tx_ring; struct tx_ring_info *tx_ring;
struct sky2_tx_le *tx_le; struct sky2_tx_le *tx_le;
struct sky2_stats tx_stats;
u16 tx_ring_size; u16 tx_ring_size;
u16 tx_cons; /* next le to check */ u16 tx_cons; /* next le to check */
u16 tx_prod; /* next le to use */ u16 tx_prod; /* next le to use */
...@@ -2221,6 +2229,7 @@ struct sky2_port { ...@@ -2221,6 +2229,7 @@ struct sky2_port {
struct rx_ring_info *rx_ring ____cacheline_aligned_in_smp; struct rx_ring_info *rx_ring ____cacheline_aligned_in_smp;
struct sky2_rx_le *rx_le; struct sky2_rx_le *rx_le;
struct sky2_stats rx_stats;
u16 rx_next; /* next re to check */ u16 rx_next; /* next re to check */
u16 rx_put; /* next le index to use */ 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,6 +2355,39 @@ static inline u32 gma_read32(struct sky2_hw *hw, unsigned port, unsigned reg)
| (u32) sky2_read16(hw, base+4) << 16; | (u32) sky2_read16(hw, base+4) << 16;
} }
static inline u64 gma_read64(struct sky2_hw *hw, unsigned port, unsigned reg)
{
unsigned base = SK_GMAC_REG(port, reg);
return (u64) sky2_read16(hw, base)
| (u64) sky2_read16(hw, base+4) << 16
| (u64) sky2_read16(hw, base+8) << 32
| (u64) sky2_read16(hw, base+12) << 48;
}
/* There is no way to atomically read32 bit values from PHY, so retry */
static inline u32 get_stats32(struct sky2_hw *hw, unsigned port, unsigned reg)
{
u32 val;
do {
val = gma_read32(hw, port, reg);
} while (gma_read32(hw, port, reg) != val);
return val;
}
static inline u64 get_stats64(struct sky2_hw *hw, unsigned port, unsigned reg)
{
u64 val;
do {
val = gma_read64(hw, port, reg);
} while (gma_read64(hw, port, reg) != val);
return val;
}
static inline void gma_write16(const struct sky2_hw *hw, unsigned port, int r, u16 v) static inline void gma_write16(const struct sky2_hw *hw, unsigned port, int r, u16 v)
{ {
sky2_write16(hw, SK_GMAC_REG(port,r), v); sky2_write16(hw, SK_GMAC_REG(port,r), v);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment