net: systemport: Track per TX ring statistics
authorFlorian Fainelli <f.fainelli@gmail.com>
Thu, 23 Mar 2017 17:36:46 +0000 (10:36 -0700)
committerDavid S. Miller <davem@davemloft.net>
Fri, 24 Mar 2017 19:53:14 +0000 (12:53 -0700)
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: Florian Fainelli <f.fainelli@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/ethernet/broadcom/bcmsysport.c
drivers/net/ethernet/broadcom/bcmsysport.h

index a68d4889f5db74d895f1bfb9e74c46bd2b892dbc..986fb05529fc0cfe6193e7f52ca604dd623a61cd 100644 (file)
@@ -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("rx_dma_failed", mib.rx_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)
@@ -338,7 +339,8 @@ static int bcm_sysport_get_sset_count(struct net_device *dev, int string_set)
                                continue;
                        j++;
                }
-               return j;
+               /* Include per-queue statistics */
+               return j + dev->num_tx_queues * NUM_SYSPORT_TXQ_STAT;
        default:
                return -EOPNOTSUPP;
        }
@@ -349,6 +351,7 @@ static void bcm_sysport_get_strings(struct net_device *dev,
 {
        struct bcm_sysport_priv *priv = netdev_priv(dev);
        const struct bcm_sysport_stats *s;
+       char buf[128];
        int i, j;
 
        switch (stringset) {
@@ -363,6 +366,18 @@ static void bcm_sysport_get_strings(struct net_device *dev,
                               ETH_GSTRING_LEN);
                        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;
        default:
                break;
@@ -418,6 +433,7 @@ static void bcm_sysport_get_stats(struct net_device *dev,
                                  struct ethtool_stats *stats, u64 *data)
 {
        struct bcm_sysport_priv *priv = netdev_priv(dev);
+       struct bcm_sysport_tx_ring *ring;
        int i, j;
 
        if (netif_running(dev))
@@ -436,6 +452,22 @@ static void bcm_sysport_get_stats(struct net_device *dev,
                data[j] = *(unsigned long *)p;
                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,
@@ -746,26 +778,26 @@ next:
        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,
                                       unsigned int *bytes_compl,
                                       unsigned int *pkts_compl)
 {
+       struct bcm_sysport_priv *priv = ring->priv;
        struct device *kdev = &priv->pdev->dev;
-       struct net_device *ndev = priv->netdev;
 
        if (cb->skb) {
-               ndev->stats.tx_bytes += cb->skb->len;
+               ring->bytes += cb->skb->len;
                *bytes_compl += cb->skb->len;
                dma_unmap_single(kdev, dma_unmap_addr(cb, dma_addr),
                                 dma_unmap_len(cb, dma_len),
                                 DMA_TO_DEVICE);
-               ndev->stats.tx_packets++;
+               ring->packets++;
                (*pkts_compl)++;
                bcm_sysport_free_cb(cb);
        /* SKB fragment */
        } 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_len(cb, dma_len), DMA_TO_DEVICE);
                dma_unmap_addr_set(cb, dma_addr, 0);
@@ -803,7 +835,7 @@ static unsigned int __bcm_sysport_tx_reclaim(struct bcm_sysport_priv *priv,
 
        while (last_tx_cn-- > 0) {
                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++;
                last_c_index++;
@@ -1632,6 +1664,24 @@ static int bcm_sysport_change_mac(struct net_device *dev, void *p)
        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)
 {
        struct bcm_sysport_priv *priv = netdev_priv(dev);
@@ -1893,6 +1943,7 @@ static const struct net_device_ops bcm_sysport_netdev_ops = {
 #ifdef CONFIG_NET_POLL_CONTROLLER
        .ndo_poll_controller    = bcm_sysport_poll_controller,
 #endif
+       .ndo_get_stats          = bcm_sysport_get_nstats,
 };
 
 #define REV_FMT        "v%2x.%02x"
index 863ddd7870b77d2ce963685098fff71211f395a8..77a51c167a694734b5983f524464e9b94725e1d7 100644 (file)
@@ -647,6 +647,9 @@ enum bcm_sysport_stat_type {
        .reg_offset = ofs, \
 }
 
+/* TX bytes and packets */
+#define NUM_SYSPORT_TXQ_STAT   2
+
 struct bcm_sysport_stats {
        char stat_string[ETH_GSTRING_LEN];
        int stat_sizeof;
@@ -690,6 +693,8 @@ struct bcm_sysport_tx_ring {
        struct bcm_sysport_cb *cbs;     /* Transmit control blocks */
        struct dma_desc *desc_cpu;      /* CPU view of the descriptor */
        struct bcm_sysport_priv *priv;  /* private context backpointer */
+       unsigned long   packets;        /* packets statistics */
+       unsigned long   bytes;          /* bytes statistics */
 };
 
 /* Driver private structure */