-
Intel 82571
零拷贝的设计与实现
本文主要阐述基于
Intel
网卡零拷贝的实现过程
,
通常情况下网络数
据包到达用户应用程序要经过如
1.
网卡的物理硬件从物理媒体
(
通常情况下网线
< br>)
上接收到得信号
(
数据帧
p>
)
首先放在网卡自己的一个
缓冲区
(
网卡
RAM)
,在这
一过程中通常要进行帧校验
(
比如
FC
S),
帧过滤等。
2.
如果网卡支持
< br>DMA
就会启动
DMA
操作,<
/p>
把收到的数据帧通过
DMA
操作放到我们事先申请好的
buffer
中,
p>
DMA
操作由硬件自动完成,当然用户要提供给
DMA
硬件操作必要的参数,包括
DMA
< br>地址,
DMA
大小等,有可能还有地址对齐等要求。
p>
DMA
的具体操作后面详细描述。
3.
这一步是
DMA
零拷贝最重要的一个环节,就是把网卡接收到的数据帧直接映射到用户层,不需
要经过内核协议栈的处理。后面详细描述。
下几个过程
:
网卡数据从网络到
Linux
内核的路径简要分析:
网卡的
主要工作原理
:
发送数据时
,
计算机把要传输的数据并行写到网卡的缓存
,
网
卡对要传输的数
据进编码
(10M
以太
网使用曼切斯特码
,100M
以太网使用差分曼切斯特码
),
串行发到传输介质上
.
< br>接收数据时
,
则相反。
对于网卡
而言,
每块网卡都有一个唯一的网络节点地址,
它是网卡生产厂
家在生产时烧入
ROM
(只
读存储芯片
)中的,我们把它叫做
MAC
地址(物理地址)
,且保证绝对不会重复。
MAC
为
48bit,
前
24
比特
由
IEEE
分配
,
p>
是需要钱买的
,
后
24bit
由网卡生产厂家自行分配
.
我们日常使用的网卡都是以太网网卡。目前网卡按其传输速度来分可分为
10M
网卡、
10
/
100
M
自适
应网卡以及千兆
(1000M)
网卡。
如果只是作为一般用途,
如日常
办公等,
比较适合使用
10M
网卡和<
/p>
10
/
100M
自适应网卡两种。如果应用于服务器等产品领域,就要选择千兆级及更高级别的网卡。
本文主要讲解的是
Intel 82571
千兆网卡的网卡驱动:
Linux
内核目录:
linux-
3.4.7/drivers/net/ethernet/intel/e1000e
关于这款网卡的硬件信息
:
更详细的信息可以通过
lspci
-vvv
查看。
从上面的信息可以看
出这块网卡是基于
pci
总线的。
数据接收流程图:
开始
pci_register_driver(&e1000_driver);
E1000_probe
alloc_etherdev
netif_napi_add
e1000_sw_init
Init_timer
INIT_WORK(&adapter->reset_task,
e1000_reset_task);
INIT_WORK(&adapter->watchdog_task,
e1000_watchdog_task);
INIT_WORK(&adapter->downshift_task,
e1000e_downshift_workaround);
INIT_WORK(&adapter->update_phy_task,
e1000e_update_phy_task);
INIT_WORK(&adapter->print_hang_task,
e1000_print_hw_hang);
大概的流程框架就是这个样子的了,
现在一边对照源码一边解析相关的功能。
e1000_init_modu
le
函数是整个网卡驱动的入口点,
在这个函数中主要做的事情是调用
p
ci_register_driver
函数向
PCI
子系统注册相关的回调函数当模块加载的时候
(
也就是执行
insmod )
会去调用相关的函数。我们看看<
/p>
e1000_driver
这个变量:
当执行完网卡注册,
然后加载网卡驱
动的时候首先执行的是
e1000_probe
函数,
在这个函数
中主要完成了:网卡模式的设置,
DMA
主从设备的设置,
网卡私有数据的分配,中断处
理函数的注册,
NA
PI
的设置,存放网卡数据帧的相关接收环,
DMA BD
p>
结构的初始化,
PCI
资源的映射,
网卡参数的检测,
MAC
,
< br>
PHY
,
< br>NVM
相关操作的回调函数的初始化,
看门狗,
硬件复位等相关的初始化。
下面来看看
e1000_probe
函数,
由于这个函数比较长我们分段讲解:
上面的
代码主要完成了设置网卡支持DMA模式,
由于有些网卡硬件必须在某些地址对齐的
p>
地方才能够执行DMA操作,
dma_set_mask
就是这个作用。
继续往下:
Pci_save_state,
保存
PCI
配置空间相关信息,
alloc_ethernetdev
分配网卡私有数据;<
/p>
得到设
备的中断号保存在
netdev-
>irq
中。由与我们操作网卡相关的寄存器是通过把网卡相关的寄
存器映射到内核内存空间,
然后通过偏移量就可以设置,
清
除相关的硬件寄存器。
下面几行
代码就是把网卡寄存器空间映射
到内核空间便于操作。
Mmio_stat
为网卡的配置空间的起始地址【以网卡为
中心看到的地址】
,
mmio_len
为长度。
Ioremap
的主要作用
是要把网卡的配置空间的资源映射到内核空间
【以
CPU
为中心看到的地
址】
。以后操作
adapter->_addr
开始的资源就相当于直接操作网卡寄存器。<
/p>
Netdev->netdev_ops = &e1000_netdev_ops;
注册网卡相关的回调函数,
主要有
e1000_open,
e1000_close
等
E100
0_set_ethtool_ops
;
这个函数主要是提供给用户的操作接口:
主要是当用户执行
ifconfig
命令的时候执行的回调
函数,显示接收数据的大小,速率,设置
IP
地址等
Ifconfig
显示的所有资料都是从这里获
取的。
现在来看看
e1000_sw_init
这个函数主要是设置网卡的一些参数,
比如接受缓冲区的长度,
网卡支持的最大帧大小,
最
小帧大小,
接收环的数目,
发送环的数目,
p>
设置中断模式,
最后关中断;
e1000_
alloc_queues
分配接受环空间大小为
sizeof(struct
e1000_ring);
上面三行主要是注册和网卡硬件
相关操作的回调函数,比如设置
mac
地址,设置网卡
Led
灯等。
Init_timer(&adapter->watchdog_timer),
初始化看门狗定时器;
下面的几个<
/p>
INIT_WORK
初始化相关的任务队列:
e1000_reset_task
复位任务,比如拔插
网
线。
E
1000e_reset
用新的值重新复位硬件。
Register_netdev
函数是
prob
e
函数的最后一步,
在这个函数中会去调用我们开始设置的回调
函数
e1000_open
在这个函数
中做进一步的初始化下面讲解
e1000_open,
在
p>
e1000_open
函
数中主要做了:<
/p>
1.
设置发送缓冲区环相关的初始化
2.
分配接受缓冲区相关的初始化
3.
物
理
PH
Y
的初始化
4.
< br>设置中断相关的寄存器,设置
DMA
相关的寄存器
5.
向内核安装中断
处理函数
使能。
大概流程是这样的:
最开始的时候我
们调用
pci_register
向
P
CI
总线注册了驱动相关的回调
函数
,
在
pci_register
完成后会
执行我们自己驱动的
e1000_probe
函数,
这里面最主要的就是
初始化一些寄存器,
还有就是
设置了
NAPI
,
然后就会调用
e1000_open
函数这里面主要初始
化
接收环。
pci_register
E1000_probe
E1000_open
e1000_request_irq
__napi_schedule
E1000_poll
e1000_clean_rx_irq
上面的流程最主要的就是最后四个函数的处理,
在这个循环中不断的收取网卡的数据帧,
然
后向上层
传递。
零拷贝需要修改的地方就主要在这里,
原来的驱动存放数
据帧的内存是通过
__netdev_alloc_skb_
ip_align
函数分配的,在
e1000_clean_r
x_irq
函数中向协议栈的上层传递
也就是说已经脱离了驱动相关的部分,
当数据到达用户层后,
这块空间由上层释放,
这也是
为什么说
NAPI
比传统的中断更有效率的原因,
当执行
NAPI
的过程中会关中断,
但是硬件会
继续收数据到我们事先分配好的环形缓冲区中,
这也是为什么我们必须先分配
一定数量的环
形缓冲区,不然的话在我们执行
NAPI
这段时间来的数据就会丢失。由于传统的中断是每接
受到一个数据包就产
生一次中断,
如果流量很大的话,
CPU
负荷较重基本都在处理中断。
现
在
的
NAPI
每次处理的数据帧数我们可以自己设定,在本驱动中
为
64
叫做权值,在
e1000_pr
obe
函数中初始化的
netif_napi_add(net
dev,
&adapter->napi,
e1000e_poll,
64
);
也就
是说每次
NAPI
可以处理
64
个数据帧,当数据帧的个数大于
64
的时候会执行多次
NAPI
,如
果小于
64
执行完
NAPI
后会开中断,把任务从
NAPI
p>
链上移除,下次中断来的时候又会关中
断,
添加任务,数据向协议栈上层传递,不停地循环。
我们现在主要是把原来
DMA
到内存的数据,
替换成由我们自己申请的
buffer
中,
然后我
们自己来管理这块
buf
fer
,由我们自己来释放这块
buffer
< br>,我们就可以把我们自己管理的缓
冲区映射到用户空间。这样就不需要经过协议栈
,就可以直接取得原始的数据帧。
主要添加的文件:
Mem_poll.c
主要完成:
1.
申请一定数量的缓冲区
2.
把这些缓冲区组织成链表的形式
3.
提供给驱动
申请缓冲区的接口
4.
提供给驱动释放缓冲区的接口
5.
把这些缓冲区映射到用户空间
-
-
-
-
-
-
-
-
-
上一篇:软件开发管理平台技术方案设计
下一篇:PSCAD 4.2 说明文件