Chinaunix首页 | 论坛 | 博客
  • 博客访问: 97193
  • 博文数量: 20
  • 博客积分: 542
  • 博客等级: 中士
  • 技术积分: 249
  • 用 户 组: 普通用户
  • 注册时间: 2012-05-10 16:47
文章分类
文章存档

2013年(1)

2012年(19)

我的朋友

分类: LINUX

2012-05-14 10:13:46

在文章<NetXen 10G网卡驱动DMA分析>中我分析了网卡驱动中的数据组织,明确了接收数据包的环形缓冲区的构成方式, 知道数据包会由网卡的firmware通过DMA自动传递到该接收环形缓冲区中.本文将分析一下,如何实现零拷贝抓包.
 

 

 

标准NetXen网卡驱动接收流程是这样子的:

在网卡驱动中有三个由struct sk_buff数据结构构造的接收环形缓冲区(NormalJumboLro)fw会将收到的数据包DMA到相应的环形缓冲区中;网卡驱动则从环形缓冲区中取出数据包,利用netif_receive_skb函数将数据包向内核网络处理层递交。

 

通过分析代码,可以看出三种环形缓冲区的如下数据:

数据包类型    缓冲区单元的个数       缓冲区单元的大小(字节)

Normal          16384                          1760

Jumbo            1024                            8062

LRO              64                                (48*1024)-512 = 47.5KB

 

可以看出,针对不同的数据包类型,缓冲区单元的个数以及大小是不同的,在此,我改造网卡驱动原有的三个环形接收缓冲区,并利用mmap将其直接向用户态暴露,这样数据包的生产者就是网卡的firmware,而消费者则成为用户态读包程序;另外,用户态数据包读取程序直接在三个环形缓冲区上轮询。

 

 

 

在内核中实现内存映射,通常被影射的内存是由一整块连续的内存构成的,可以直接映射到用户空间;而netxen网卡中环形缓冲区(一共三个,这里以一个为例进行讨论)则是由多个离散的struct sk_buff构成的,映射的方法就需要有所改变。

 

后来经过分析发现,映射离散缓冲区并不成问题,问题在于内核中进行mmap必须以页为单位,而构成网卡环形缓冲区的sk_buff是在系统的skb slab池中获取的,其内存格局不为我们所控制,而且显然不服从页边界,这就否定了离散映射的方案。

 

现在我考虑的思路是这样子的:

(1) 为每一种数据包事先分配一个足够大(单元大小*个数)的连续内核缓冲区,自己控制sk_buff的分配:也就是在驱动调用dev_alloc_skb等内核中类似分配sk_buff的地方,修改其行为,改成从我们的连续内核缓冲区中分配。这样就解决了页边界的问题。

       (2) 将分配的连续内核缓冲区映射到用户空间。

 

 

 – user & kernel

 

环形缓冲区在用户空间和内核空间的同步,可以采用如下的方案:

(1) 在驱动中实现poll函数,在必要时阻塞用户进程

(2) 用户态读包程序利用poll系统调用轮询数据包

 

零拷贝的要点包含两个:DMA和内存映射. 本文以内核树中netxen网卡驱动程序为例,讨论了零拷贝的实现方案,抛砖引玉,希望对您有所帮助.

阅读(2361) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~