Commit 30defeb2 authored by Florian Fainelli's avatar Florian Fainelli Committed by David S. Miller

net: systemport: Track per TX ring statistics

bcm_sysport_tx_reclaim_one() is currently summing TX bytes/packets in a
way that is not SMP friendly, mutliples CPUs could run
bcm_sysport_tx_reclaim_one() independently and still update
stats->tx_bytes and stats->tx_packets, cloberring the other CPUs
statistics.

Fix this by tracking per TX rings the number of bytes, packets,
dropped and errors statistics, and provide a bcm_sysport_get_nstats()
function which aggregates everything and returns a consistent output.
Signed-off-by: default avatarFlorian Fainelli <f.fainelli@gmail.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 12459cbd
...@@ -284,6 +284,7 @@ static const struct bcm_sysport_stats bcm_sysport_gstrings_stats[] = { ...@@ -284,6 +284,7 @@ static const struct bcm_sysport_stats bcm_sysport_gstrings_stats[] = {
STAT_MIB_SOFT("alloc_rx_buff_failed", mib.alloc_rx_buff_failed), STAT_MIB_SOFT("alloc_rx_buff_failed", mib.alloc_rx_buff_failed),
STAT_MIB_SOFT("rx_dma_failed", mib.rx_dma_failed), STAT_MIB_SOFT("rx_dma_failed", mib.rx_dma_failed),
STAT_MIB_SOFT("tx_dma_failed", mib.tx_dma_failed), STAT_MIB_SOFT("tx_dma_failed", mib.tx_dma_failed),
/* Per TX-queue statistics are dynamically appended */
}; };
#define BCM_SYSPORT_STATS_LEN ARRAY_SIZE(bcm_sysport_gstrings_stats) #define BCM_SYSPORT_STATS_LEN ARRAY_SIZE(bcm_sysport_gstrings_stats)
...@@ -338,7 +339,8 @@ static int bcm_sysport_get_sset_count(struct net_device *dev, int string_set) ...@@ -338,7 +339,8 @@ static int bcm_sysport_get_sset_count(struct net_device *dev, int string_set)
continue; continue;
j++; j++;
} }
return j; /* Include per-queue statistics */
return j + dev->num_tx_queues * NUM_SYSPORT_TXQ_STAT;
default: default:
return -EOPNOTSUPP; return -EOPNOTSUPP;
} }
...@@ -349,6 +351,7 @@ static void bcm_sysport_get_strings(struct net_device *dev, ...@@ -349,6 +351,7 @@ static void bcm_sysport_get_strings(struct net_device *dev,
{ {
struct bcm_sysport_priv *priv = netdev_priv(dev); struct bcm_sysport_priv *priv = netdev_priv(dev);
const struct bcm_sysport_stats *s; const struct bcm_sysport_stats *s;
char buf[128];
int i, j; int i, j;
switch (stringset) { switch (stringset) {
...@@ -363,6 +366,18 @@ static void bcm_sysport_get_strings(struct net_device *dev, ...@@ -363,6 +366,18 @@ static void bcm_sysport_get_strings(struct net_device *dev,
ETH_GSTRING_LEN); ETH_GSTRING_LEN);
j++; j++;
} }
for (i = 0; i < dev->num_tx_queues; i++) {
snprintf(buf, sizeof(buf), "txq%d_packets", i);
memcpy(data + j * ETH_GSTRING_LEN, buf,
ETH_GSTRING_LEN);
j++;
snprintf(buf, sizeof(buf), "txq%d_bytes", i);
memcpy(data + j * ETH_GSTRING_LEN, buf,
ETH_GSTRING_LEN);
j++;
}
break; break;
default: default:
break; break;
...@@ -418,6 +433,7 @@ static void bcm_sysport_get_stats(struct net_device *dev, ...@@ -418,6 +433,7 @@ static void bcm_sysport_get_stats(struct net_device *dev,
struct ethtool_stats *stats, u64 *data) struct ethtool_stats *stats, u64 *data)
{ {
struct bcm_sysport_priv *priv = netdev_priv(dev); struct bcm_sysport_priv *priv = netdev_priv(dev);
struct bcm_sysport_tx_ring *ring;
int i, j; int i, j;
if (netif_running(dev)) if (netif_running(dev))
...@@ -436,6 +452,22 @@ static void bcm_sysport_get_stats(struct net_device *dev, ...@@ -436,6 +452,22 @@ static void bcm_sysport_get_stats(struct net_device *dev,
data[j] = *(unsigned long *)p; data[j] = *(unsigned long *)p;
j++; j++;
} }
/* For SYSTEMPORT Lite since we have holes in our statistics, j would
* be equal to BCM_SYSPORT_STATS_LEN at the end of the loop, but it
* needs to point to how many total statistics we have minus the
* number of per TX queue statistics
*/
j = bcm_sysport_get_sset_count(dev, ETH_SS_STATS) -
dev->num_tx_queues * NUM_SYSPORT_TXQ_STAT;
for (i = 0; i < dev->num_tx_queues; i++) {
ring = &priv->tx_rings[i];
data[j] = ring->packets;
j++;
data[j] = ring->bytes;
j++;
}
} }
static void bcm_sysport_get_wol(struct net_device *dev, static void bcm_sysport_get_wol(struct net_device *dev,
...@@ -746,26 +778,26 @@ static unsigned int bcm_sysport_desc_rx(struct bcm_sysport_priv *priv, ...@@ -746,26 +778,26 @@ static unsigned int bcm_sysport_desc_rx(struct bcm_sysport_priv *priv,
return processed; return processed;
} }
static void bcm_sysport_tx_reclaim_one(struct bcm_sysport_priv *priv, static void bcm_sysport_tx_reclaim_one(struct bcm_sysport_tx_ring *ring,
struct bcm_sysport_cb *cb, struct bcm_sysport_cb *cb,
unsigned int *bytes_compl, unsigned int *bytes_compl,
unsigned int *pkts_compl) unsigned int *pkts_compl)
{ {
struct bcm_sysport_priv *priv = ring->priv;
struct device *kdev = &priv->pdev->dev; struct device *kdev = &priv->pdev->dev;
struct net_device *ndev = priv->netdev;
if (cb->skb) { if (cb->skb) {
ndev->stats.tx_bytes += cb->skb->len; ring->bytes += cb->skb->len;
*bytes_compl += cb->skb->len; *bytes_compl += cb->skb->len;
dma_unmap_single(kdev, dma_unmap_addr(cb, dma_addr), dma_unmap_single(kdev, dma_unmap_addr(cb, dma_addr),
dma_unmap_len(cb, dma_len), dma_unmap_len(cb, dma_len),
DMA_TO_DEVICE); DMA_TO_DEVICE);
ndev->stats.tx_packets++; ring->packets++;
(*pkts_compl)++; (*pkts_compl)++;
bcm_sysport_free_cb(cb); bcm_sysport_free_cb(cb);
/* SKB fragment */ /* SKB fragment */
} else if (dma_unmap_addr(cb, dma_addr)) { } else if (dma_unmap_addr(cb, dma_addr)) {
ndev->stats.tx_bytes += dma_unmap_len(cb, dma_len); ring->bytes += dma_unmap_len(cb, dma_len);
dma_unmap_page(kdev, dma_unmap_addr(cb, dma_addr), dma_unmap_page(kdev, dma_unmap_addr(cb, dma_addr),
dma_unmap_len(cb, dma_len), DMA_TO_DEVICE); dma_unmap_len(cb, dma_len), DMA_TO_DEVICE);
dma_unmap_addr_set(cb, dma_addr, 0); dma_unmap_addr_set(cb, dma_addr, 0);
...@@ -803,7 +835,7 @@ static unsigned int __bcm_sysport_tx_reclaim(struct bcm_sysport_priv *priv, ...@@ -803,7 +835,7 @@ static unsigned int __bcm_sysport_tx_reclaim(struct bcm_sysport_priv *priv,
while (last_tx_cn-- > 0) { while (last_tx_cn-- > 0) {
cb = ring->cbs + last_c_index; cb = ring->cbs + last_c_index;
bcm_sysport_tx_reclaim_one(priv, cb, &bytes_compl, &pkts_compl); bcm_sysport_tx_reclaim_one(ring, cb, &bytes_compl, &pkts_compl);
ring->desc_count++; ring->desc_count++;
last_c_index++; last_c_index++;
...@@ -1632,6 +1664,24 @@ static int bcm_sysport_change_mac(struct net_device *dev, void *p) ...@@ -1632,6 +1664,24 @@ static int bcm_sysport_change_mac(struct net_device *dev, void *p)
return 0; return 0;
} }
static struct net_device_stats *bcm_sysport_get_nstats(struct net_device *dev)
{
struct bcm_sysport_priv *priv = netdev_priv(dev);
unsigned long tx_bytes = 0, tx_packets = 0;
struct bcm_sysport_tx_ring *ring;
unsigned int q;
for (q = 0; q < dev->num_tx_queues; q++) {
ring = &priv->tx_rings[q];
tx_bytes += ring->bytes;
tx_packets += ring->packets;
}
dev->stats.tx_bytes = tx_bytes;
dev->stats.tx_packets = tx_packets;
return &dev->stats;
}
static void bcm_sysport_netif_start(struct net_device *dev) static void bcm_sysport_netif_start(struct net_device *dev)
{ {
struct bcm_sysport_priv *priv = netdev_priv(dev); struct bcm_sysport_priv *priv = netdev_priv(dev);
...@@ -1893,6 +1943,7 @@ static const struct net_device_ops bcm_sysport_netdev_ops = { ...@@ -1893,6 +1943,7 @@ static const struct net_device_ops bcm_sysport_netdev_ops = {
#ifdef CONFIG_NET_POLL_CONTROLLER #ifdef CONFIG_NET_POLL_CONTROLLER
.ndo_poll_controller = bcm_sysport_poll_controller, .ndo_poll_controller = bcm_sysport_poll_controller,
#endif #endif
.ndo_get_stats = bcm_sysport_get_nstats,
}; };
#define REV_FMT "v%2x.%02x" #define REV_FMT "v%2x.%02x"
......
...@@ -647,6 +647,9 @@ enum bcm_sysport_stat_type { ...@@ -647,6 +647,9 @@ enum bcm_sysport_stat_type {
.reg_offset = ofs, \ .reg_offset = ofs, \
} }
/* TX bytes and packets */
#define NUM_SYSPORT_TXQ_STAT 2
struct bcm_sysport_stats { struct bcm_sysport_stats {
char stat_string[ETH_GSTRING_LEN]; char stat_string[ETH_GSTRING_LEN];
int stat_sizeof; int stat_sizeof;
...@@ -690,6 +693,8 @@ struct bcm_sysport_tx_ring { ...@@ -690,6 +693,8 @@ struct bcm_sysport_tx_ring {
struct bcm_sysport_cb *cbs; /* Transmit control blocks */ struct bcm_sysport_cb *cbs; /* Transmit control blocks */
struct dma_desc *desc_cpu; /* CPU view of the descriptor */ struct dma_desc *desc_cpu; /* CPU view of the descriptor */
struct bcm_sysport_priv *priv; /* private context backpointer */ struct bcm_sysport_priv *priv; /* private context backpointer */
unsigned long packets; /* packets statistics */
unsigned long bytes; /* bytes statistics */
}; };
/* Driver private structure */ /* Driver private structure */
......
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