DPDK踩坑记

Posted yuanyun_elber

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了DPDK踩坑记相关的知识,希望对你有一定的参考价值。

公司的新产品是一款服务器端的网卡芯片,支持各种密码学计算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
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

############################################################################

符合预期

以上是关于DPDK踩坑记的主要内容,如果未能解决你的问题,请参考以下文章

DPDK踩坑记

Spring @Transactional踩坑记

HttpWebRequest 改为 HttpClient 踩坑记-请求头设置

FastDFS踩坑记

iptables踩坑记

windows container 踩坑记