Skip to content

Commit

Permalink
sky2: implement 64 bit stats
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
stephen hemminger authored and David S. Miller committed Jan 1, 2011
1 parent a016892 commit 0885a30
Show file tree
Hide file tree
Showing 2 changed files with 109 additions and 36 deletions.
103 changes: 67 additions & 36 deletions drivers/net/sky2.c
Original file line number Diff line number Diff line change
Expand Up @@ -1917,8 +1917,10 @@ static void sky2_tx_complete(struct sky2_port *sky2, u16 done)
netif_printk(sky2, tx_done, KERN_DEBUG, dev,
"tx done %u\n", idx);

dev->stats.tx_packets++;
dev->stats.tx_bytes += skb->len;
u64_stats_update_begin(&sky2->tx_stats.syncp);
++sky2->tx_stats.packets;
sky2->tx_stats.bytes += skb->len;
u64_stats_update_end(&sky2->tx_stats.syncp);

re->skb = NULL;
dev_kfree_skb_any(skb);
Expand Down Expand Up @@ -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 != count)
goto len_error;
goto error;

okay:
if (length < copybreak)
Expand All @@ -2475,34 +2477,13 @@ static struct sk_buff *sky2_receive(struct net_device *dev,

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:
++dev->stats.rx_errors;
if (status & GMR_FS_RX_FF_OV) {
dev->stats.rx_over_errors++;
goto resubmit;
}

if (net_ratelimit())
netif_info(sky2, rx_err, dev,
"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;
}

Expand Down Expand Up @@ -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,
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);

dev->stats.rx_packets += packets;
dev->stats.rx_bytes += bytes;
dev->last_rx = jiffies;
sky2_rx_update(netdev_priv(dev), rxqaddr[port]);
}
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->last_rx = jiffies;
sky2_rx_update(netdev_priv(dev), rxqaddr[port]);
}

static void sky2_rx_checksum(struct sky2_port *sky2, u32 status)
Expand Down Expand Up @@ -3626,13 +3612,11 @@ static void sky2_phy_stats(struct sky2_port *sky2, u64 * data, unsigned count)
unsigned port = sky2->port;
int i;

data[0] = (u64) gma_read32(hw, port, GM_TXO_OK_HI) << 32
| (u64) gma_read32(hw, port, GM_TXO_OK_LO);
data[1] = (u64) gma_read32(hw, port, GM_RXO_OK_HI) << 32
| (u64) gma_read32(hw, port, GM_RXO_OK_LO);
data[0] = get_stats64(hw, port, GM_TXO_OK_LO);
data[1] = get_stats64(hw, port, GM_RXO_OK_LO);

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)
Expand Down Expand Up @@ -3750,6 +3734,51 @@ static void sky2_set_multicast(struct net_device *dev)
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
* ethtool and that is always under RTNL mutex
*/
Expand Down Expand Up @@ -4524,6 +4553,7 @@ static const struct net_device_ops sky2_netdev_ops[2] = {
.ndo_set_multicast_list = sky2_set_multicast,
.ndo_change_mtu = sky2_change_mtu,
.ndo_tx_timeout = sky2_tx_timeout,
.ndo_get_stats64 = sky2_get_stats,
#ifdef SKY2_VLAN_TAG_USED
.ndo_vlan_rx_register = sky2_vlan_rx_register,
#endif
Expand All @@ -4541,6 +4571,7 @@ static const struct net_device_ops sky2_netdev_ops[2] = {
.ndo_set_multicast_list = sky2_set_multicast,
.ndo_change_mtu = sky2_change_mtu,
.ndo_tx_timeout = sky2_tx_timeout,
.ndo_get_stats64 = sky2_get_stats,
#ifdef SKY2_VLAN_TAG_USED
.ndo_vlan_rx_register = sky2_vlan_rx_register,
#endif
Expand Down
42 changes: 42 additions & 0 deletions drivers/net/sky2.h
Original file line number Diff line number Diff line change
Expand Up @@ -2200,6 +2200,12 @@ enum flow_control {
FC_BOTH = 3,
};

struct sky2_stats {
struct u64_stats_sync syncp;
u64 packets;
u64 bytes;
};

struct sky2_port {
struct sky2_hw *hw;
struct net_device *netdev;
Expand All @@ -2209,6 +2215,8 @@ struct sky2_port {

struct tx_ring_info *tx_ring;
struct sky2_tx_le *tx_le;
struct sky2_stats tx_stats;

u16 tx_ring_size;
u16 tx_cons; /* next le to check */
u16 tx_prod; /* next le to use */
Expand All @@ -2221,6 +2229,7 @@ struct sky2_port {

struct rx_ring_info *rx_ring ____cacheline_aligned_in_smp;
struct sky2_rx_le *rx_le;
struct sky2_stats rx_stats;

u16 rx_next; /* next re to check */
u16 rx_put; /* next le index to use */
Expand Down Expand Up @@ -2346,6 +2355,39 @@ static inline u32 gma_read32(struct sky2_hw *hw, unsigned port, unsigned reg)
| (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)
{
sky2_write16(hw, SK_GMAC_REG(port,r), v);
Expand Down

0 comments on commit 0885a30

Please sign in to comment.