UPGW interface 流量统计功能

需求分析

目前, UPGW 命令行可以通过 show all 命令查看到各接口和其所收到的数据包个数统计,但缺少接口读取时因内存满导致错误的计数。经分析,在 DPDK 已有相关接口可以获取,增加计数显示即可。

image-1

代码设计

  1. 修改存放数据包统计的结构体,增加 rx full 情况下的收包计数变量:

    image-2

  2. 修改 server 端 show all 命令,增加计数变量:

    image-3

  3. 修改 client 端 show all 命令,增加计数显示:

    image-4

相关代码分析

NES 中存放接口信息的结构体:

nes_dev_t 结构体存放了接口相关信息,每个接口对应一个结构体;全局变量 nes_io_device ( file: nes_io.c ) 是由 nes_devt_t 组成的链表,维护 nes 中所有接口信息。

nes_ctrl_dev_t 结构体存放了 nes_ctrl 使用的接口信息,每个接口对应一个;全局变量 dev_list ( file: nes_ctrl.c ) 链表串联所有 nes_ctrl_dev_t 结构体。

nes_dev_t 和 nes_ctrl_dev_t 是一一对应的关系,可通过 dev_stats 和 dev_ptr 两个指针互指查到。

image-5

DPDK 接口收发包统计 API :

DPDK 统计流量数据存放在 rte_eth_stats 结构体中,并提供两个 API 来读写该结构( server 端直接调用的就是该 API ),其中写操作只有重置,没有实时写。

// file: libs/dpdk-18.08/lib/librte_ethdev/rte_ethdev.h

/**
 * A structure used to retrieve statistics for an Ethernet port.
 * Not all statistics fields in struct rte_eth_stats are supported
 * by any type of network interface card (NIC). If any statistics
 * field is not supported, its value is 0.
 */
struct rte_eth_stats {
	uint64_t ipackets;  /**< Total number of successfully received packets. */
	uint64_t opackets;  /**< Total number of successfully transmitted packets.*/
	uint64_t ibytes;    /**< Total number of successfully received bytes. */
	uint64_t obytes;    /**< Total number of successfully transmitted bytes. */
	uint64_t imissed;
	/**< Total of RX packets dropped by the HW,
	 * because there are no available buffer (i.e. RX queues are full).
	 */
	uint64_t ierrors;   /**< Total number of erroneous received packets. */
	uint64_t oerrors;   /**< Total number of failed transmitted packets. */
	uint64_t rx_nombuf; /**< Total number of RX mbuf allocation failures. */
	uint64_t q_ipackets[RTE_ETHDEV_QUEUE_STAT_CNTRS];
	/**< Total number of queue RX packets. */
	uint64_t q_opackets[RTE_ETHDEV_QUEUE_STAT_CNTRS];
	/**< Total number of queue TX packets. */
	uint64_t q_ibytes[RTE_ETHDEV_QUEUE_STAT_CNTRS];
	/**< Total number of successfully received queue bytes. */
	uint64_t q_obytes[RTE_ETHDEV_QUEUE_STAT_CNTRS];
	/**< Total number of successfully transmitted queue bytes. */
	uint64_t q_errors[RTE_ETHDEV_QUEUE_STAT_CNTRS];
	/**< Total number of queue packets received that are dropped. */
};

rte_eth_stats 成员作用:

  1. ipackets : 成功接收的数据包总数。( rte_ethdev.h :255 )

  2. opackets: 成功发送的数据包总数。( rte_ethdev.h :256 )

  3. ibytes: 成功接收的数据字节数。 ( rte_ethdev.h :257)

  4. obytes: 成功发送的数据字节数。( rte_ethdev.h :258 )

  5. imissed: 由于 buffer 不够用而被硬件丢弃的数据包总数 。( rte_ethdev.h :259 )

  6. ierrors: 接收失败的数据包总数。( rte_ethdev.h :263 )

  7. oerrors: 发送失败的数据包总数。( rte_ehtdev.h :264 )

  8. rx_nombuf: RX mbuf 分配故障总数。( rte_ethdev.h :265 )

  9. q_ipcakets: 队列读取的数据包总数。( rte_ethdev.h :267 )

  10. o_opackets: 队列发送的数据包总数。( rte_ethdev.h: 269 )

  11. q_ibytes: 队列成功接收的数据包总数。( rte_ethdev.h :271 )

  12. q_obytes: 队列成功发送的数据包总数。( rte_ethdev.h :273 )

  13. q_errors: 被丢弃的队列已收数据包总数。( rte_ethdev.h :275 )

/**
 * Retrieve the general I/O statistics of an Ethernet device.
 *
 * @param port_id
 *   The port identifier of the Ethernet device.
 * @param stats
 *   A pointer to a structure of type *rte_eth_stats* to be filled with
 *   the values of device counters for the following set of statistics:
 *   - *ipackets* with the total of successfully received packets.
 *   - *opackets* with the total of successfully transmitted packets.
 *   - *ibytes*   with the total of successfully received bytes.
 *   - *obytes*   with the total of successfully transmitted bytes.
 *   - *ierrors*  with the total of erroneous received packets.
 *   - *oerrors*  with the total of failed transmitted packets.
 * @return
 *   Zero if successful. Non-zero otherwise.
 */
int rte_eth_stats_get(uint16_t port_id, struct rte_eth_stats *stats);

/**
 * Reset the general I/O statistics of an Ethernet device.
 *
 * @param port_id
 *   The port identifier of the Ethernet device.
 * @return
 *   - (0) if device notified to reset stats.
 *   - (-ENOTSUP) if hardware doesn't support.
 *   - (-ENODEV) if *port_id* invalid.
 */
int rte_eth_stats_reset(uint16_t port_id);

成员的具体赋值,在驱动目录中可以看到( libs/dpdk-18.08/drivers/net/ ):

// file: libs/dpdk-18.08/drivers/net/e1000/em_ethdev.c
static int eth_em_stats_get(struct rte_eth_dev *dev, struct rte_eth_stats *rte_stats);
static void eth_em_stats_reset(struct rte_eth_dev *dev);
// file: libs/dpdk-18.08/drivers/net/af_packet/rte_eth_af_packet.c

static int eth_stats_get(struct rte_eth_dev *dev, struct rte_eth_stats *igb_stats);
static void eth_stats_reset(struct rte_eth_dev *dev);
// file: libs/dpdk-18.08/drivers/bus/dpaa/base/fman/fman_hw.c

void fman_if_stats_get(struct fman_if *p, struct rte_eth_stats *stats);
void fman_if_stats_get_all(struct fman_if *p, uint64_t *value, int n);
void fman_if_stats_reset(struct fman_if *p);

Restful API 中获取的字段与 DPDK 接口字段的对应关系:

restful API 请求 interface state 获取的返回字段如下:

image-6

各字段含义如下:

[                                          # 接口以 json 格式返回全部 interface 信息。
    {                                      # 单个 interface 信息用 {} 括起。
        "ID": 0,                           # ID 是接口编号,与 nts.cfg 中的 [PORT] 编号一致。
        "drp_bytes_1": 0,                  # drp_bytes_1 表示物理网卡发送失败所丢弃的字节数。
        "drp_cnt_1": 0,                    # drp_cnt_1 表示物理网卡发送失败的包数。
        "drp_cnt_2": 0,                    # drp_cnt_2 表示物理网卡读取失败的包数。
        "drp_cnt_3": 0,                    # drp_cnt_3 表示 RX buffer 不足导致读取失败的包数。
        "ip_fragment": 0,                  # ip_fragment 表示 ip 分片包数。
        "mac": "52:54:0:86:52:e9",         # mac 表示接口的物理网卡,由 DPDK 读取获得。
        "name": "upstream",                # name 表示接口的名称,与 nts.cfg 中的 name 字段一致。
        "rcv_bytes": 0,                    # rcv_bytes 表示接口读取的字节数。
        "rcv_cnt": 0,                      # rcv_cnt 表示接口读取的包数。
        "snd_bytes": 0,                    # snd_bytes 表示接口发送的字节数。
        "snd_cnt": 0                       # snd_cnt 表示接口发送的包数。
    },
    {
        "ID": 1,
        "drp_bytes_1": 0,
        "drp_cnt_1": 0,
        "drp_cnt_2": 0,
        "drp_cnt_3": 0,
        "ip_fragment": 0,
        "mac": "52:54:0:a1:13:32",
        "name": "downstream",
        "rcv_bytes": 0,
        "rcv_cnt": 0,
        "snd_bytes": 0,
        "snd_cnt": 0
    },
    {
        "ID": 2,
        "drp_bytes_1": 0,
        "drp_cnt_1": 0,
        "drp_cnt_2": 0,
        "drp_cnt_3": 0,
        "ip_fragment": 0,
        "mac": "52:54:0:64:16:48",
        "name": "LBP",
        "rcv_bytes": 0,
        "rcv_cnt": 0,
        "snd_bytes": 0,
        "snd_cnt": 0
    }
]

reference

rte_eth_rx_burst 函数介绍 : https://doc.dpdk.org/api/rte__ethdev_8h.html#a3e7d76a451b46348686ea97d6367f102

rte_eth_stats_get 函数介绍:https://doc.dpdk.org/api/rte__ethdev_8h.html#adec226574c53ae413252c9b15f6f4bab

DPDK 收发包分析:https://blog.csdn.net/u014787815/article/details/109019273