当前位置: 首页 > 工具软件 > ethtool > 使用案例 >

ethtool查看网卡统计信息的流程

金高轩
2023-12-01

        使用 ethtool 工具可以查看网卡硬件的统计信息,比如,收包数、发包数、单播数、多播数等等。具体操作如下:

$ ethtool -S enp0s3f0
NIC statistics:
     mmc_tx_octetcount_gb: 224841119
     mmc_tx_framecount_gb: 1295134
     mmc_tx_broadcastframe_g: 72032
     mmc_tx_multicastframe_g: 448
     mmc_tx_64_octets_gb: 76558
...

        上面是 Gmac 网卡的输出信息,比较多,只截取了其中一部分。对于不同的网卡,上面输出的内容会有所不同,但基本格式是一样的,因为这是 ethtool 设定的输出格式,和具体的网卡无关。共分为两列,第一列是统计项的名称,第二列是对应的数值。之所以会有不同,是因为统计项的名称是网卡驱动给出的,ethtool 只负责显示。

        在内核中,每块网卡都对应一个 struct net_device 结构体,该结构体包含一个 const struct ethtool_ops *ethtool_ops 成员,该结构体成员中包含一系列函数指针,由具体的网卡驱动实现,用于配合内核实现 ethtool 相关操作。对于 gmac 网卡而言,struct ethtool_ops 的赋值如下:

static const struct ethtool_ops stmmac_ethtool_ops = { 
    ...    
    .get_ethtool_stats = stmmac_get_ethtool_stats,       
    .get_strings = stmmac_get_strings,
    .get_wol = stmmac_get_wol,
    .set_wol = stmmac_set_wol,
    .get_eee = stmmac_ethtool_op_get_eee,
    .set_eee = stmmac_ethtool_op_set_eee,
    .get_sset_count = stmmac_get_sset_count,
    ...
}; 

        在实现上,ethtool 只需要做三件事,并且都需要内核的配合。ethtool 通过 ioctl 与内核通信,ioctl 的 request 参数是 SIOCETHTOOL。
        1)使用 ETHTOOL_GSSET_INFO 命令字请求内核,获得统计项的数量。在内核中会通过以下流程调用到 Gmac 驱动的 stmmac_get_sset_count() 函数:

ethtool_get_sset_info()
  __ethtool_get_sset_count() 
    ops->get_sset_count()

        stmmac_get_sset_count() 的实现如下:

static int stmmac_get_sset_count(struct net_device *netdev, int sset)                                                                                                                                                                         
{
    struct stmmac_priv *priv = netdev_priv(netdev);
    int i, len, safety_len = 0;
 
    switch (sset) { 
    case ETH_SS_STATS:
        len = STMMAC_STATS_LEN;
 
        if (priv->dma_cap.rmon)
            len += STMMAC_MMC_STATS_LEN;
        if (priv->dma_cap.asp) {
            for (i = 0; i < STMMAC_SAFETY_FEAT_SIZE; i++) {
                if (!stmmac_safety_feat_dump(priv,
                            &priv->sstats, i,
                            NULL, NULL))
                    safety_len++;
            }
 
            len += safety_len;
        }
 
        return len;
    default: 
        return -EOPNOTSUPP;
    }          
}

        2)使用 ETHTOOL_GSTRINGS 命令字请求内核,获得统计项的名称。在内核中会通过以下流程调用到 Gmac 驱动的 stmmac_get_strings() 函数:

ethtool_get_strings()
  __ethtool_get_strings()
    ops->get_strings()

        stmmac_get_strings() 的实现如下:

static void stmmac_get_strings(struct net_device *dev, u32 stringset, u8 *data)
{  
    int i;
    u8 *p = data;
    struct stmmac_priv *priv = netdev_priv(dev);
   
    switch (stringset) {
    case ETH_SS_STATS:
        if (priv->dma_cap.asp) {
            for (i = 0; i < STMMAC_SAFETY_FEAT_SIZE; i++) {
                const char *desc;
                if (!stmmac_safety_feat_dump(priv,
                            &priv->sstats, i,
                            NULL, &desc)) {
                    memcpy(p, desc, ETH_GSTRING_LEN);
                    p += ETH_GSTRING_LEN;
                }
            }
        }
        if (priv->dma_cap.rmon)
            for (i = 0; i < STMMAC_MMC_STATS_LEN; i++) {
                memcpy(p, stmmac_mmc[i].stat_string,
                       ETH_GSTRING_LEN);
                p += ETH_GSTRING_LEN;
            }
        for (i = 0; i < STMMAC_STATS_LEN; i++) {
            memcpy(p, stmmac_gstrings_stats[i].stat_string,
                ETH_GSTRING_LEN);
            p += ETH_GSTRING_LEN;
        }
        break;
    default:
        WARN_ON(1);
        break;
    }
}

        3)使用 ETHTOOL_GSTATS 命令字请求内核,获得统计项的值。在内核中会通过以下流程调用到 Gmac 驱动的 stmmac_get_ethtool_stats() 函数:

ethtool_get_stats()
  ops->get_ethtool_stats()

        stmmac_get_ethtool_stats() 的实现如下:

static void stmmac_get_ethtool_stats(struct net_device *dev,
                 struct ethtool_stats *dummy, u64 *data)
{
    struct stmmac_priv *priv = netdev_priv(dev);
    u32 rx_queues_count = priv->plat->rx_queues_to_use;
    u32 tx_queues_count = priv->plat->tx_queues_to_use;
    unsigned long count;
    int i, j = 0, ret;
 
    if (priv->dma_cap.asp) {
        for (i = 0; i < STMMAC_SAFETY_FEAT_SIZE; i++) {
            if (!stmmac_safety_feat_dump(priv, &priv->sstats, i,
                        &count, NULL))
                data[j++] = count;
        }         
    }
 
    /* Update the DMA HW counters for dwmac10/100 */
    ret = stmmac_dma_diagnostic_fr(priv, &dev->stats, (void *) &priv->xstats,
            priv->ioaddr);
    if (ret) {
        /* If supported, for new GMAC chips expose the MMC counters */
        if (priv->dma_cap.rmon) {
            dwmac_mmc_read(priv->mmcaddr, &priv->mmc);
 
            for (i = 0; i < STMMAC_MMC_STATS_LEN; i++) {
                char *p;
                p = (char *)priv + stmmac_mmc[i].stat_offset;
 
                data[j++] = (stmmac_mmc[i].sizeof_stat ==
                         sizeof(u64)) ? (*(u64 *)p) :
                         (*(u32 *)p);
            }
        }         
        if (priv->eee_enabled) {
            int val = phy_get_eee_err(dev->phydev);
            if (val)
                priv->xstats.phy_eee_wakeup_error_n = val;
        }    
 
        if (priv->synopsys_id >= DWMAC_CORE_3_50)
            stmmac_mac_debug(priv, priv->ioaddr,
                    (void *)&priv->xstats,
                    rx_queues_count, tx_queues_count);
    }             
    for (i = 0; i < STMMAC_STATS_LEN; i++) {
        char *p = (char *)priv + stmmac_gstrings_stats[i].stat_offset;
        data[j++] = (stmmac_gstrings_stats[i].sizeof_stat ==
                 sizeof(u64)) ? (*(u64 *)p) : (*(u32 *)p);
    }    
}

        上述实现中,通过 dwmac_mmc_read() 函数读取 Gmac 网卡 MMC 模块中寄存器的值。

 类似资料: