Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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: Florian Fainelli <f.fainelli@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by

Florian Fainelli and committed by
David S. Miller
30defeb2 12459cbd

+63 -7
+58 -7
drivers/net/ethernet/broadcom/bcmsysport.c
··· 284 284 STAT_MIB_SOFT("alloc_rx_buff_failed", mib.alloc_rx_buff_failed), 285 285 STAT_MIB_SOFT("rx_dma_failed", mib.rx_dma_failed), 286 286 STAT_MIB_SOFT("tx_dma_failed", mib.tx_dma_failed), 287 + /* Per TX-queue statistics are dynamically appended */ 287 288 }; 288 289 289 290 #define BCM_SYSPORT_STATS_LEN ARRAY_SIZE(bcm_sysport_gstrings_stats) ··· 339 338 continue; 340 339 j++; 341 340 } 342 - return j; 341 + /* Include per-queue statistics */ 342 + return j + dev->num_tx_queues * NUM_SYSPORT_TXQ_STAT; 343 343 default: 344 344 return -EOPNOTSUPP; 345 345 } ··· 351 349 { 352 350 struct bcm_sysport_priv *priv = netdev_priv(dev); 353 351 const struct bcm_sysport_stats *s; 352 + char buf[128]; 354 353 int i, j; 355 354 356 355 switch (stringset) { ··· 363 360 continue; 364 361 365 362 memcpy(data + j * ETH_GSTRING_LEN, s->stat_string, 363 + ETH_GSTRING_LEN); 364 + j++; 365 + } 366 + 367 + for (i = 0; i < dev->num_tx_queues; i++) { 368 + snprintf(buf, sizeof(buf), "txq%d_packets", i); 369 + memcpy(data + j * ETH_GSTRING_LEN, buf, 370 + ETH_GSTRING_LEN); 371 + j++; 372 + 373 + snprintf(buf, sizeof(buf), "txq%d_bytes", i); 374 + memcpy(data + j * ETH_GSTRING_LEN, buf, 366 375 ETH_GSTRING_LEN); 367 376 j++; 368 377 } ··· 433 418 struct ethtool_stats *stats, u64 *data) 434 419 { 435 420 struct bcm_sysport_priv *priv = netdev_priv(dev); 421 + struct bcm_sysport_tx_ring *ring; 436 422 int i, j; 437 423 438 424 if (netif_running(dev)) ··· 450 434 p = (char *)priv; 451 435 p += s->stat_offset; 452 436 data[j] = *(unsigned long *)p; 437 + j++; 438 + } 439 + 440 + /* For SYSTEMPORT Lite since we have holes in our statistics, j would 441 + * be equal to BCM_SYSPORT_STATS_LEN at the end of the loop, but it 442 + * needs to point to how many total statistics we have minus the 443 + * number of per TX queue statistics 444 + */ 445 + j = bcm_sysport_get_sset_count(dev, ETH_SS_STATS) - 446 + dev->num_tx_queues * NUM_SYSPORT_TXQ_STAT; 447 + 448 + for (i = 0; i < dev->num_tx_queues; i++) { 449 + ring = &priv->tx_rings[i]; 450 + data[j] = ring->packets; 451 + j++; 452 + data[j] = ring->bytes; 453 453 j++; 454 454 } 455 455 } ··· 778 746 return processed; 779 747 } 780 748 781 - static void bcm_sysport_tx_reclaim_one(struct bcm_sysport_priv *priv, 749 + static void bcm_sysport_tx_reclaim_one(struct bcm_sysport_tx_ring *ring, 782 750 struct bcm_sysport_cb *cb, 783 751 unsigned int *bytes_compl, 784 752 unsigned int *pkts_compl) 785 753 { 754 + struct bcm_sysport_priv *priv = ring->priv; 786 755 struct device *kdev = &priv->pdev->dev; 787 - struct net_device *ndev = priv->netdev; 788 756 789 757 if (cb->skb) { 790 - ndev->stats.tx_bytes += cb->skb->len; 758 + ring->bytes += cb->skb->len; 791 759 *bytes_compl += cb->skb->len; 792 760 dma_unmap_single(kdev, dma_unmap_addr(cb, dma_addr), 793 761 dma_unmap_len(cb, dma_len), 794 762 DMA_TO_DEVICE); 795 - ndev->stats.tx_packets++; 763 + ring->packets++; 796 764 (*pkts_compl)++; 797 765 bcm_sysport_free_cb(cb); 798 766 /* SKB fragment */ 799 767 } else if (dma_unmap_addr(cb, dma_addr)) { 800 - ndev->stats.tx_bytes += dma_unmap_len(cb, dma_len); 768 + ring->bytes += dma_unmap_len(cb, dma_len); 801 769 dma_unmap_page(kdev, dma_unmap_addr(cb, dma_addr), 802 770 dma_unmap_len(cb, dma_len), DMA_TO_DEVICE); 803 771 dma_unmap_addr_set(cb, dma_addr, 0); ··· 835 803 836 804 while (last_tx_cn-- > 0) { 837 805 cb = ring->cbs + last_c_index; 838 - bcm_sysport_tx_reclaim_one(priv, cb, &bytes_compl, &pkts_compl); 806 + bcm_sysport_tx_reclaim_one(ring, cb, &bytes_compl, &pkts_compl); 839 807 840 808 ring->desc_count++; 841 809 last_c_index++; ··· 1664 1632 return 0; 1665 1633 } 1666 1634 1635 + static struct net_device_stats *bcm_sysport_get_nstats(struct net_device *dev) 1636 + { 1637 + struct bcm_sysport_priv *priv = netdev_priv(dev); 1638 + unsigned long tx_bytes = 0, tx_packets = 0; 1639 + struct bcm_sysport_tx_ring *ring; 1640 + unsigned int q; 1641 + 1642 + for (q = 0; q < dev->num_tx_queues; q++) { 1643 + ring = &priv->tx_rings[q]; 1644 + tx_bytes += ring->bytes; 1645 + tx_packets += ring->packets; 1646 + } 1647 + 1648 + dev->stats.tx_bytes = tx_bytes; 1649 + dev->stats.tx_packets = tx_packets; 1650 + return &dev->stats; 1651 + } 1652 + 1667 1653 static void bcm_sysport_netif_start(struct net_device *dev) 1668 1654 { 1669 1655 struct bcm_sysport_priv *priv = netdev_priv(dev); ··· 1943 1893 #ifdef CONFIG_NET_POLL_CONTROLLER 1944 1894 .ndo_poll_controller = bcm_sysport_poll_controller, 1945 1895 #endif 1896 + .ndo_get_stats = bcm_sysport_get_nstats, 1946 1897 }; 1947 1898 1948 1899 #define REV_FMT "v%2x.%02x"
+5
drivers/net/ethernet/broadcom/bcmsysport.h
··· 647 647 .reg_offset = ofs, \ 648 648 } 649 649 650 + /* TX bytes and packets */ 651 + #define NUM_SYSPORT_TXQ_STAT 2 652 + 650 653 struct bcm_sysport_stats { 651 654 char stat_string[ETH_GSTRING_LEN]; 652 655 int stat_sizeof; ··· 693 690 struct bcm_sysport_cb *cbs; /* Transmit control blocks */ 694 691 struct dma_desc *desc_cpu; /* CPU view of the descriptor */ 695 692 struct bcm_sysport_priv *priv; /* private context backpointer */ 693 + unsigned long packets; /* packets statistics */ 694 + unsigned long bytes; /* bytes statistics */ 696 695 }; 697 696 698 697 /* Driver private structure */