公司的新产品是一款服务器端的网卡芯片,支持各种密码学计算offload,是清华大学的可重构结构,还挺牛逼的,不过再怎么牛逼,这还是一块网卡芯片,上网是主要的功能,所以最近入坑DPDK了。之所以说入坑,是因为网络方面完全是小白,学习的过程就是不断填坑的过程。dpdp网上的资料已经挺多的了,我主要把自己学习过程中遇到的问题记录下来,如果觉得很小儿科的大神可以飘过了......
硬件环境: (主机Intel(R) Core(TM) i5-4590 CPU @ 3.30GHz + 我司的n10芯片网卡)* 2, 一套作为待测机,一套作为陪测机,我们的网卡是一个四口版本,也就是可以ifconfig会显示出4张网卡,暂时我们只连接了其中的一路,即陪测机的port3 连接到待测机的port2。
操作系统
: centos,Linux localhost.localdomain 3.10.0-1160.el7.x86_64
dpdk版本: 20.02
dpdk pktgen版本:20.02.0
N10的DPDK补丁目前还不支持meson编译,只能make编译,所以dpdk版本只能20.11之前的,20.11之后都是meson编译的,我们选择了20.02版本
wget http://fast.dpdk.org/rel/dpdk-20.02.1.tar.xz
xz -d dpdk-20.02.1.tar.xz
tar -xf dpdk-20.02.1.tar
cd dpdk-stable-20.02.1
export RTE_SDK=`pwd` ##系统临时环境变量,ssh断开重连后需要重新输入此命令
git apply ../tsrn10-pmd/20.02/0001-net-tsrn10-add-PMD-skeleton.patch
make -j 8 install T=x86_64-native-linuxapp-gcc CONFIG_RTE_EAL_IGB_UIO=y
如果编译提示numa.h找不到的话
yum install numactl-devel
devbind.py -b igb_uio 01:00.1
然后再跑一下testpmd,提示
Cause: Creation of mbuf pool for socket 0 failed: Invalid argument
echo 2048 > /sys/kernel/mm/hugepages/hugepages-2048kB/nr_hugepages
就可以运行了
接下来在陪测机安装pktgen用来发包,注意版本和dpdk适配
wget http://git.dpdk.org/apps/pktgen-dpdk/snapshot/pktgen-dpdk-pktgen-20.02.0.tar.gz
tar xvf pktgen-dpdk-pktgen-20.02.0.tar.gz
cd pktgen-dpdk-pktgen-20.02.0
export RTE_SDK=/bak/dpdk-stable-20.02.1/
export RTE_TARGET=x86_64-native-linuxapp-gcc
make install
提示缺少lua.h,在3.5以上的pktgen-dpdk编译时,lua的版本必须要在5.3以上。而centos7yum源中自带的lua包只支持到5.1
使用源码包编译安装:
lua官网下载地址:https://www.lua.org/ftp/
tar -xvf #解压lua
cd lua-5.4.3
make linux #编译链接库
make install #安装到系统中同时修改系统环境变量
make local #使当前用户
fatal error: pcap/pcap.h: No such file or directory
安装lipcap库:
yum install libpcap-devel
接下来陪测机和待测机分别设置好hugepage,加载igb_uio.ko后,使用dpdk-devbind.py进行网卡绑定就可以跑testpmd和pktgen了。
mkdir -p /dev/hugepages
mountpoint -q /dev/hugepages || mount -t hugetlbfs nodev /dev/hugepages
echo 2048 > /sys/devices/system/node/node0/hugepages/hugepages-2048kB/nr_hugepages
modprobe uio
insmod ../dpdk-stable-20.02.1/x86_64-native-linuxapp-gcc/build/kernel/linux/igb_uio/igb_uio.ko
ifconfig enp1s1f1 down
../dpdk-stable-20.02.1/usertools/dpdk-devbind.py -b igb_uio 01:00.0
../dpdk-stable-20.02.1/usertools/dpdk-devbind.py -b igb_uio 01:00.1
#./app/x86_64-native-linuxapp-gcc/pktgen -l 0-3 -n 1 -- -P -m "[1].3" #陪测机端跑这条,发包
# ../dpdk-stable-20.02.1/x86_64-native-linuxapp-gcc/app/testpmd -l 0-3 -n 4 -- -i #待测机端跑这条,收包
试试看,好像有点不对,陪测机的pktgen端可以看到发包数如下
而待测机的testpmd端显示rx-packets是0
testpmd> show port stats 2
######################## NIC statistics for port 2 ########################
RX-packets: 0 RX-missed: 0 RX-bytes: 0
RX-errors: 0
RX-nombuf: 0
TX-packets: 0 TX-errors: 0 TX-bytes: 0
Throughput (since last show)
Rx-pps: 0 Rx-bps: 0
Tx-pps: 0 Tx-bps: 0
############################################################################
但是用show port xstats 2看是有收到包的,数字也对得上
testpmd> show port xstats 2
###### NIC extended statistics for port 2
rx_good_packets: 0
tx_good_packets: 0
rx_good_bytes: 0
tx_good_bytes: 0
rx_missed_errors: 0
rx_errors: 0
tx_errors: 0
rx_mbuf_allocation_errors: 0
rx_q0packets: 0
rx_q0bytes: 0
rx_q0errors: 0
tx_q0packets: 0
tx_q0bytes: 0
Mac Local Fault: 0
Mac remote Fault: 0
Rx good bad Pkts: 103731328
Rx good bad bytes: 6638804992
Rx good Pkts: 103731328
RX good Bytes: 6638804992
Rx Broadcast Pkts: 0
Rx Multicast Pkts: 0
Rx Crc Frames Err Pkts: 0
Rx len Err with Crc err: 0
Rx jabber Error : 0
Rx len Err Without Other Error: 0
Rx Len Shorter 64Bytes Without Err: 0
Rx Len Oversize Max Support Err: 0
Rx 64Bytes Frame Num: 103731328
Rx 65Bytes To 127Bytes Frame Num: 0
Rx 128Bytes To 255Bytes Frame Num: 0
代码中看上去应该是如下函数响应show port stats命令的
static void tsrn10_stats_get(struct rte_eth_dev *dev,
struct rte_eth_stats *stats)
#endif
struct tsrn10_eth_port *port = TSRN10_DEV_TO_PORT(dev);
struct tsrn10_hw *hw = TSRN10_DEV_TO_HW(dev);
struct rte_eth_dev_data *data = dev->data;
uint64_t rx_miss = 0;
int i = 0;
PMD_INIT_FUNC_TRACE();
memset(stats, 0, sizeof(*stats));
tsrn10_get_hw_stats(dev);
for (i = 0; i < data->nb_rx_queues; i++) {
stats->q_ipackets[i] = ((struct tsrn10_rx_queue **)
(data->rx_queues))[i]->stats.ipackets;
stats->q_ibytes[i] = ((struct tsrn10_rx_queue **)
(data->rx_queues))[i]->stats.ibytes;
stats->ipackets += stats->q_ipackets[i];//ipackets应该就是收包数
stats->ibytes += stats->q_ibytes[i];
调试一下,的确这个值是0
这是怎么回事呢,翻翻代码,看看其他网卡是怎么更新这个值的:
static int
ixgbe_dev_stats_get(struct rte_eth_dev *dev, struct rte_eth_stats *stats)
struct ixgbe_hw *hw =
IXGBE_DEV_PRIVATE_TO_HW(dev->data->dev_private);
struct ixgbe_hw_stats *hw_stats =
IXGBE_DEV_PRIVATE_TO_STATS(dev->data->dev_private);
struct ixgbe_macsec_stats *macsec_stats =
IXGBE_DEV_PRIVATE_TO_MACSEC_STATS(
dev->data->dev_private);
uint64_t total_missed_rx, total_qbrc, total_qprc, total_qprdc;
unsigned i;
total_missed_rx = 0;
total_qbrc = 0;
total_qprc = 0;
total_qprdc = 0;
ixgbe_read_stats_registers(hw, hw_stats, macsec_stats, &total_missed_rx,
&total_qbrc, &total_qprc, &total_qprdc);
//来源是这个hw_stats,而hw_stats是从硬件寄存器读取的
if (stats == NULL)
return -EINVAL;
/* Fill out the rte_eth_stats statistics structure */
stats->ipackets = total_qprc;
stats->ibytes = total_qbrc;
stats->opackets = hw_stats->gptc;
stats->obytes = hw_stats->gotc;
for (i = 0; i < IXGBE_QUEUE_STAT_COUNTERS; i++) {
stats->q_ipackets[i] = hw_stats->qprc[i];
stats->q_opackets[i] = hw_stats->qptc[i];
stats->q_ibytes[i] = hw_stats->qbrc[i];
stats->q_obytes[i] = hw_stats->qbtc[i];
stats->q_errors[i] = hw_stats->qprdc[i];
ixgbe_read_stats_registers是从硬件去去读寄存器更新这个值。
找了很多网卡,都是这种从硬件读取的方式,类似的,我们也有硬件寄存器记录一些信息,在tsrn10_get_hw_stats这个函数中会去读取:
tsrn10_get_mmc_info(hw, p_id, stats, ptr);
在执行show port xstats all命令的时候就是得到这些信息
这里面的rx_good_pkts,rx_all_pkts 都有,但是没有区分队列的pkt统计
找到一个avp_ethdev.c和我们类似也是软件更新的
avp_dev_stats_get(struct rte_eth_dev *eth_dev, struct rte_eth_stats *stats)
struct avp_dev *avp = AVP_DEV_PRIVATE_TO_HW(eth_dev->data->dev_private);
unsigned int i;
for (i = 0; i < avp->num_rx_queues; i++) {
struct avp_queue *rxq = avp->dev_data->rx_queues[i];
if (rxq) {
stats->ipackets += rxq->packets;
stats->ibytes += rxq->bytes;
stats->ierrors += rxq->errors;
stats->q_ipackets[i] += rxq->packets;
stats->q_ibytes[i] += rxq->bytes;
stats->q_errors[i] += rxq->errors;
for (i = 0; i < avp->num_tx_queues; i++) {
struct avp_queue *txq = avp->dev_data->tx_queues[i];
if (txq) {
stats->opackets += txq->packets;
stats->obytes += txq->bytes;
stats->oerrors += txq->errors;
stats->q_opackets[i] += txq->packets;
stats->q_obytes[i] += txq->bytes;
return 0;
在avp_recv_pkts中会去增加
rxq->packets += count;
而在我们的tsrn10_recv_pkts函数中
tsrn10_clean_rx_ring也会调用
rxq->stats.ipackets += nb_rx;
加断点调试一下,发现居然没有进入tsrn10_recv_pkts函数,再往前追溯rte_eth_rx_burst 也么有调用
原来问题在这个地方,testpmd命令后输入start后,终于进入了rte_eth_rx_burst ,单步的话进入回调是tsrn10_rx_burst_simple
-->tsrn10_recv_pkts-->tsrn10_clean_rx_ring,但是在下面代码的位置提前返回了,没有执行到下面更新ipackets的地方
static inline int tsrn10_clean_rx_ring(struct tsrn10_rx_queue *rxq)
struct tsrn10_rxsw_entry *rx_swbd;
uint32_t state_cmd[CACHE_FETCH_RX];
uint32_t pkt_len[CACHE_FETCH_RX] = {0};
volatile struct tsrn10_rx_desc *rxbd;
struct rte_mbuf *nmb;
int nb_dd, nb_rx = 0;
uint32_t status;
int i, j;
rxbd = &rxq->rx_bdr[rxq->next_to_clean];
status = rxbd->wb.vlan_cmd;
if (!(status & rte_cpu_to_le_32(TSRN10_CMD_DD)))
return 0;
这个应该就是PMD(poll mode)的含义了,轮询的情况下很多时候是没有收到包的,另外加断点
(gdb) b tsrn10_rxtx.c:1453
重新start一下之后,停在了断点,证明我们的直觉是正确的。
不过丢包率似乎有点高,做一组实验对比一下:
在pktgen端,默认64字节包长,设置
Pktgen:/> set 3 rate 10
设置10%的发包率,因为我们是10G的网口,差不多1Gbit/s,统计如下
######################## NIC statistics for port 2 ########################
RX-packets: 539040 RX-missed: 460960 RX-bytes: 32342400
RX-errors: 0
RX-nombuf: 0
TX-packets: 0 TX-errors: 0 TX-bytes: 0
Throughput (since last show)
Rx-pps: 27689 Rx-bps: 13291088
Tx-pps: 0 Tx-bps: 0
############################################################################
丢包率很高
如果在pktgen端设置1500字节包长,即使发包率是100%,丢包率也大大下降了
######################## NIC statistics for port 2 ########################
RX-packets: 971248 RX-missed: 5466 RX-bytes: 1452987008
RX-errors: 0
RX-nombuf: 0
TX-packets: 0 TX-errors: 0 TX-bytes: 0
Throughput (since last show)
Rx-pps: 127232 Rx-bps: 1522720256
Tx-pps: 0 Tx-bps: 0
############################################################################
公司的新产品是一款服务器端的网卡芯片,支持各种密码学计算offload,是清华大学的可重构结构,还挺牛逼的,不过再怎么牛逼,这还是一块网卡芯片,上网是主要的功能,所以最近入坑DPDK了。之所以说入坑,是因为网络方面完全是小白,学习的过程就是不断填坑的过程。dpdp网上的资料已经挺多的了,我主要把自己学习过程中遇到的问题记录下来,如果觉得很小儿科的大神可以飘过了......硬件环境:(主机Intel(R) Core(TM) i5-4590 CPU @ 3.30GHz + 我司的n10芯片网卡)*..
本文介绍了数据平面开发工具包(DPDK)TestPMD应用程序,展示了如何构建和配置TestPMD, 以及如何用它来检查使用DPDK的不同网络设备的性能和功能。
TestPMD是一个使用DPDK软件包分发的参考应用程序。其主要目的是在网络接口的以太网端口之间转发数据包。此外,用户还可以用TestPMD尝试一些不同驱动程序的功能,例如RSS,过滤器和英特尔®以太网流量控制器(Intel® Ether...
DPDK官方文档学习线路初学DPDKDPDK文档阅读线路DPDK官方文档阅读顺序
初学DPDK
刚开始接触某项新技能的时候,总是容易一头雾水,不知道从哪里入手。初学DPDK时亦会如此,下面就介绍下DPDK的官方推荐学习线路图,目的是帮助DPDK的初学者们顺利度过懵逼期。
DPDK文档阅读线路
在没有入门指导资料的情况下,阅读官方文档是最好的途径。
DPDK官方文档阅读顺序
推荐的DPDK官方文档阅读顺序如下:
发行版本说明(Release Notes):提供发行版本信息,包括支持的特性,限制,修复的问题,已知问题等。通常还提供FAQ,可以对发行版本有一个初步认知。当然,也可以在初步掌握DPDK
由于工作需要,需要在DPDK上用testpmd应用程序进行测试。本文主要为了记录自己需要掌握的testpmd的操作命令。
官方文档:http://doc.dpdk.org/guides/testpmd_app_ug/intro.html
Introduction
testpmd对dpdk来说是dpdk工具包的一部分,主要是用来对dpdk的测试,当然其中也包括其他的一些功能,就工作而言,我需要理...
DPDK(Data Plane Development Kit)是一个用于构建高性能数据平面应用程序的开源工具集。它提供了一组优化的库和驱动程序,可以在网络和协议栈的处理过程中加速数据包的转发和处理。DPDK 最初是为 Linux 环境设计开发的,但后来也提供了一些实验性的 Windows 版本。
DPDK 在 Windows 上的支持仍然处于实验性阶段。虽然可以在 Windows 上运行 DPDK,但仍然面临一些挑战和限制。首先,由于 Windows 内核和驱动程序的限制,与 Linux 环境相比,DPDK 在 Windows 上的性能可能会有所下降。其次,Windows 版本的 DPDK 目前仅支持少数的网络设备供应商,这意味着不是所有的网络硬件都能与 Windows 版本的 DPDK 兼容。此外,Windows 版本的 DPDK 需要使用特殊的驱动程序和堆栈,可能需要进行额外的配置和调整。
尽管 Windows 版本的 DPDK 还有一些限制,但它仍然为在 Windows 环境下构建高性能数据平面应用程序提供了一些可能性。对于现有的 Windows 网络设备供应商和应用程序开发者来说,DPDK 提供了一种加速数据包处理的方法,可以提高数据平面的性能和吞吐量。
总之,DPDK 是一个用于构建高性能数据平面应用程序的工具集,目前也有一些实验性的 Windows 版本。尽管 Windows 版本的 DPDK 还存在一些挑战和限制,但它仍然为 Windows 环境下的高性能数据平面应用程序开发提供了一些可能性。