[PATCH net-next 2/2] ifb: support ethtools stats
From: <hidden>
Date: 2021-11-25 02:34:19
Subsystem:
networking drivers, the rest · Maintainers:
Andrew Lunn, "David S. Miller", Eric Dumazet, Jakub Kicinski, Paolo Abeni, Linus Torvalds
From: Tonghao Zhang <redacted> With this feature, we can use the ethtools to get tx/rx queues stats. This patch, introduce the ifb_update_q_stats helper to update the queues stats, and ifb_q_stats to simplify the codes. Cc: Jakub Kicinski <kuba@kernel.org> Cc: "David S. Miller" <davem@davemloft.net> Signed-off-by: Tonghao Zhang <redacted> --- drivers/net/ifb.c | 146 +++++++++++++++++++++++++++++++++++++--------- 1 file changed, 120 insertions(+), 26 deletions(-)
diff --git a/drivers/net/ifb.c b/drivers/net/ifb.c
index d9078ce041c4..e5b0eeadca20 100644
--- a/drivers/net/ifb.c
+++ b/drivers/net/ifb.c@@ -40,30 +40,54 @@ #define DRV_VERSION "1.0" #define TX_Q_LIMIT 32 +struct ifb_q_stats { + u64 packets; + u64 bytes; + struct u64_stats_sync sync; +}; + struct ifb_q_private { struct net_device *dev; struct tasklet_struct ifb_tasklet; int tasklet_pending; int txqnum; struct sk_buff_head rq; - u64 rx_packets; - u64 rx_bytes; - struct u64_stats_sync rsync; - - struct u64_stats_sync tsync; - u64 tx_packets; - u64 tx_bytes; struct sk_buff_head tq; + struct ifb_q_stats rx_stats; + struct ifb_q_stats tx_stats; } ____cacheline_aligned_in_smp; struct ifb_dev_private { struct ifb_q_private *tx_private; }; +/* For ethtools. */ +struct ifb_q_stats_desc { + char desc[ETH_GSTRING_LEN]; + size_t offset; +}; + +#define IFB_Q_STAT(m) offsetof(struct ifb_q_stats, m) + +static const struct ifb_q_stats_desc ifb_q_stats_desc[] = { + { "packets", IFB_Q_STAT(packets) }, + { "bytes", IFB_Q_STAT(bytes) }, +}; + +#define IFB_Q_STATS_LEN ARRAY_SIZE(ifb_q_stats_desc) + static netdev_tx_t ifb_xmit(struct sk_buff *skb, struct net_device *dev); static int ifb_open(struct net_device *dev); static int ifb_close(struct net_device *dev); +static inline void ifb_update_q_stats(struct ifb_q_stats *stats, int len) +{ + u64_stats_update_begin(&stats->sync); + stats->packets++; + stats->bytes += len; + u64_stats_update_end(&stats->sync); +} + static void ifb_ri_tasklet(struct tasklet_struct *t) { struct ifb_q_private *txp = from_tasklet(txp, t, ifb_tasklet);
@@ -87,10 +111,7 @@ static void ifb_ri_tasklet(struct tasklet_struct *t) #endif nf_skip_egress(skb, true); - u64_stats_update_begin(&txp->tsync); - txp->tx_packets++; - txp->tx_bytes += skb->len; - u64_stats_update_end(&txp->tsync); + ifb_update_q_stats(&txp->tx_stats, skb->len); rcu_read_lock(); skb->dev = dev_get_by_index_rcu(dev_net(txp->dev), skb->skb_iif);
@@ -143,18 +164,18 @@ static void ifb_stats64(struct net_device *dev, for (i = 0; i < dev->num_tx_queues; i++,txp++) { do { - start = u64_stats_fetch_begin_irq(&txp->rsync); - packets = txp->rx_packets; - bytes = txp->rx_bytes; - } while (u64_stats_fetch_retry_irq(&txp->rsync, start)); + start = u64_stats_fetch_begin_irq(&txp->rx_stats.sync); + packets = txp->rx_stats.packets; + bytes = txp->rx_stats.bytes; + } while (u64_stats_fetch_retry_irq(&txp->rx_stats.sync, start)); stats->rx_packets += packets; stats->rx_bytes += bytes; do { - start = u64_stats_fetch_begin_irq(&txp->tsync); - packets = txp->tx_packets; - bytes = txp->tx_bytes; - } while (u64_stats_fetch_retry_irq(&txp->tsync, start)); + start = u64_stats_fetch_begin_irq(&txp->tx_stats.sync); + packets = txp->tx_stats.packets; + bytes = txp->tx_stats.bytes; + } while (u64_stats_fetch_retry_irq(&txp->tx_stats.sync, start)); stats->tx_packets += packets; stats->tx_bytes += bytes; }
@@ -177,8 +198,8 @@ static int ifb_dev_init(struct net_device *dev) txp->dev = dev; __skb_queue_head_init(&txp->rq); __skb_queue_head_init(&txp->tq); - u64_stats_init(&txp->rsync); - u64_stats_init(&txp->tsync); + u64_stats_init(&txp->rx_stats.sync); + u64_stats_init(&txp->tx_stats.sync); tasklet_setup(&txp->ifb_tasklet, ifb_ri_tasklet); netif_tx_start_queue(netdev_get_tx_queue(dev, i)); }
@@ -191,6 +212,79 @@ static void ifb_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info strlcpy(info->version, DRV_VERSION, sizeof(info->version)); } +static void ifb_get_strings(struct net_device *dev, u32 stringset, u8 *buf) +{ + u8 *p = buf; + int i, j; + + switch(stringset) { + case ETH_SS_STATS: + for (i = 0; i < dev->real_num_rx_queues; i++) { + for (j = 0; j < IFB_Q_STATS_LEN; j++) + ethtool_sprintf(&p, "rx_queue_%u_%.18s", + i, ifb_q_stats_desc[j].desc); + } + + for (i = 0; i < dev->real_num_tx_queues; i++) { + for (j = 0; j < IFB_Q_STATS_LEN; j++) + ethtool_sprintf(&p, "tx_queue_%u_%.18s", + i, ifb_q_stats_desc[j].desc); + } + break; + } +} + +static int ifb_get_sset_count(struct net_device *dev, int sset) +{ + switch (sset) { + case ETH_SS_STATS: + return IFB_Q_STATS_LEN * (dev->real_num_rx_queues + + dev->real_num_tx_queues); + default: + return -EOPNOTSUPP; + } +} + +static void ifb_get_ethtool_stats(struct net_device *dev, + struct ethtool_stats *stats, u64 *data) +{ + struct ifb_dev_private *dp = netdev_priv(dev); + unsigned int start; + size_t offset; + int idx = 0; + int i, j; + + for (i = 0; i < dev->real_num_rx_queues; i++) { + struct ifb_q_private *txp = dp->tx_private + i; + struct ifb_q_stats *rq_stats = &txp->rx_stats; + void *stats_base = (void *)&txp->rx_stats;; + + do { + start = u64_stats_fetch_begin_irq(&rq_stats->sync); + for (j = 0; j < IFB_Q_STATS_LEN; j++) { + offset = ifb_q_stats_desc[j].offset; + data[idx + j] = *(u64 *)(stats_base + offset); + } + } while (u64_stats_fetch_retry_irq(&rq_stats->sync, start)); + idx += IFB_Q_STATS_LEN; + } + + for (i = 0; i < dev->real_num_tx_queues; i++) { + struct ifb_q_private *txp = dp->tx_private + i; + struct ifb_q_stats *tq_stats = &txp->tx_stats; + void *stats_base = (void *)&txp->tx_stats;; + + do { + start = u64_stats_fetch_begin_irq(&tq_stats->sync); + for (j = 0; j < IFB_Q_STATS_LEN; j++) { + offset = ifb_q_stats_desc[j].offset; + data[idx + j] = *(u64 *)(stats_base + offset); + } + } while (u64_stats_fetch_retry_irq(&tq_stats->sync, start)); + idx += IFB_Q_STATS_LEN; + } +} + static const struct net_device_ops ifb_netdev_ops = { .ndo_open = ifb_open, .ndo_stop = ifb_close,
@@ -201,7 +295,10 @@ static const struct net_device_ops ifb_netdev_ops = { }; static const struct ethtool_ops ifb_ethtool_ops = { - .get_drvinfo = ifb_get_drvinfo, + .get_drvinfo = ifb_get_drvinfo, + .get_strings = ifb_get_strings, + .get_sset_count = ifb_get_sset_count, + .get_ethtool_stats = ifb_get_ethtool_stats, }; #define IFB_FEATURES (NETIF_F_HW_CSUM | NETIF_F_SG | NETIF_F_FRAGLIST | \
@@ -257,10 +354,7 @@ static netdev_tx_t ifb_xmit(struct sk_buff *skb, struct net_device *dev) struct ifb_dev_private *dp = netdev_priv(dev); struct ifb_q_private *txp = dp->tx_private + skb_get_queue_mapping(skb); - u64_stats_update_begin(&txp->rsync); - txp->rx_packets++; - txp->rx_bytes += skb->len; - u64_stats_update_end(&txp->rsync); + ifb_update_q_stats(&txp->rx_stats, skb->len); if (!skb->redirected || !skb->skb_iif) { dev_kfree_skb(skb);
--
2.27.0