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

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

符合预期

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
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 环境下的高性能数据平面应用程序开发提供了一些可能性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值