UPGW interface 流量统计功能
需求分析
目前, UPGW 命令行可以通过 show all
命令查看到各接口和其所收到的数据包个数统计,但缺少接口读取时因内存满导致错误的计数。经分析,在 DPDK 已有相关接口可以获取,增加计数显示即可。
代码设计
修改存放数据包统计的结构体,增加 rx full 情况下的收包计数变量:
修改 server 端 show all 命令,增加计数变量:
修改 client 端 show all 命令,增加计数显示:
相关代码分析
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 两个指针互指查到。
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 成员作用:
ipackets : 成功接收的数据包总数。( rte_ethdev.h :255 )
opackets: 成功发送的数据包总数。( rte_ethdev.h :256 )
ibytes: 成功接收的数据字节数。 ( rte_ethdev.h :257)
obytes: 成功发送的数据字节数。( rte_ethdev.h :258 )
imissed: 由于 buffer 不够用而被硬件丢弃的数据包总数 。( rte_ethdev.h :259 )
ierrors: 接收失败的数据包总数。( rte_ethdev.h :263 )
oerrors: 发送失败的数据包总数。( rte_ehtdev.h :264 )
rx_nombuf: RX mbuf 分配故障总数。( rte_ethdev.h :265 )
q_ipcakets: 队列读取的数据包总数。( rte_ethdev.h :267 )
o_opackets: 队列发送的数据包总数。( rte_ethdev.h: 269 )
q_ibytes: 队列成功接收的数据包总数。( rte_ethdev.h :271 )
q_obytes: 队列成功发送的数据包总数。( rte_ethdev.h :273 )
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 获取的返回字段如下:
各字段含义如下:
[ # 接口以 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