DMA 网卡零拷贝实现的设计与
Intel 82571 零拷贝的设计与实现
    本文主要阐述基于Intel网卡零拷贝的实现过程, 通常情况下网络数据包到达用户应用程序要经过如下几个过程: 刮油最狠的四种减肥食物
1. 网卡的物理硬件从物理媒体(通常情况下网线)上接收到得信号(数据帧)首先放在网卡自己的一个缓冲区(网卡RAM),在这一过程中通常要进行帧校验(比如FCS), 帧过滤等。
2. 如果网卡支持DMA就会启动DMA操作, 把收到的数据帧通过DMA操作放到我们事先申请好的buffer中,DMA操作由硬件自动完成,当然用户要提供给DMA硬件操作必要的参数,包括DMA地址,DMA大小等,有可能还有地址对齐等要求。
DMA的具体操作后面详细描述。
3. 这一步是DMA零拷贝最重要的一个环节,就是把网卡接收到的数据帧直接映射到用户层,不需要经过内核协议栈的处理。后面详细描述。
网卡数据从网络到Linux内核的路径简要分析:
      网卡的主要工作原理:发送数据时,计算机把要传输的数据并行写到网卡的缓存,网卡对要传输的数据进编码(10M以太网使用曼切斯,100M以太网使用差分曼切斯),串行发到传输介质上.接收数据时,则相反。对于网卡而言,每块网卡都有一个唯一的网络节点地址,它是网卡生产厂家在生产时烧入ROM(只读存储芯片)中的,我们把它叫做MAC地址(物理地址),且保证绝对不会重复。MAC为48bit,前24比特由IEEE分配,是需要钱买的,后24bit由网卡生产厂家自行分配.
我们日常使用的网卡都是以太网网卡。目前网卡按其传输速度来分可分为10M网卡、10/100M自适应网卡以及千兆(1000M)网卡。如果只是作为一般用途,如日常办公等,比较适合使用10M网卡和10/100M自适应网卡两种。如果应用于服务器等产品领域,就要选择千兆级及更高级别的网卡。
   
    本文主要讲解的是Intel 82571 千兆网卡的网卡驱动:
        Linux 内核目录:linux-3.4.7/drivers/net/ethernet/intel/e1000e
       
关于这款卡的硬件信息:
   
更详细的信息可以通过 lspci -vvv查看。
从上面的信息可以看出这块网卡是基于pci总线的。
数据接收流程图:
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_module函数是整个网卡驱动的入口点, 在这个函数中主要做的事情是调用
pci_register_driver函数向PCI子系统注册相关的回调函数当模块加载的时候(也就是执行insmod modname.ko)会去调用相关的函数。我们看看e1000_driver这个变量:
当执行完网卡注册,然后加载网卡驱动的时候首先执行的是e1000_probe函数,在这个函数中主要完成了:网卡模式的设置, DMA主从设备的设置, 网卡私有数据的分配,中断处理函数的注册,NAPI的设置,存放网卡数据帧的相关接收环,DMA BD结构的初始化,PCI资源的映射,网卡参数的检测,MAC, PHY, NVM相关操作的回调函数的初始化,看门狗,硬件复位等相关的初始化。
下面来看看e1000_probe函数, 由于这个函数比较长我们分段讲解:
   
   
马桶堵了如何通
哈尔滨特小吃
上面的代码主要完成了设置网卡支持DMA模式,由于有些网卡硬件必须在某些地址对齐的地方才能够执行DMA操作,dma_set_mask就是这个作用。 继续往下:
Pci_save_state, 保存PCI配置空间相关信息, alloc_ethernetdev 分配网卡私有数据;得到设备的中断号保存在netdev->irq中。由与我们操作网卡相关的寄存器是通过把网卡相关的寄存器映射到内核内存空间,然后通过偏移量就可以设置,清除相关的硬件寄存器。下面几行代码就是把网卡寄存器空间映射到内核空间便于操作。
Mmio_stat 为网卡的配置空间的起始地址【以网卡为中心看到的地址】,mmio_len为长度。
Ioremap的主要作用是要把网卡的配置空间的资源映射到内核空间【以CPU为中心看到的地址】。以后操作adapter->hw.hw_addr开始的资源就相当于直接操作网卡寄存器。
Netdev->netdev_ops = &e1000_netdev_ops;
注册网卡相关的回调函数, 主要有e1000_open, e1000_close等
E1000_set_ethtool_ops; 这个函数主要是提供给用户的操作接口: 主要是当用户执行ifconfig命令的时候执行的回调函数,显示接收数据的大小,速率,设置IP地址等
Ifconfig显示的所有资料都是从这里获取的。
现在来看看e1000_sw_init
这个函数主要是设置网卡的一些参数,比如接受缓冲区的长度,网卡支持的最大帧大小,最小帧大小,接收环的数目,发送环的数目,设置中断模式,最后关中断;e1000_alloc_queues
分配接受环空间大小为sizeof(struct e1000_ring);
上面三行主要是注册和网卡硬件相关操作的回调函数,比如设置mac地址,设置网卡Led灯等。
劳动法 年假Init_timer(&adapter->watchdog_timer), 初始化看门狗定时器;
下面的几个INIT_WORK初始化相关的任务队列: e1000_reset_task复位任务,比如拔插网线。
E1000e_reset用新的值重新复位硬件。
Register_netdev函数是probe函数的最后一步,在这个函数中会去调用我们开始设置的回调函数e1000_open在这个函数中做进一步的初始化下面讲解e1000_open, 在e1000_open函数中主要做了:1. 设置发送缓冲区环相关的初始化 2. 分配接受缓冲区相关的初始化 3.物理PHY的初始化 4. 设置中断相关的寄存器,设置DMA相关的寄存器 5. 向内核安装中断处理函数 6.NAPI使能。
大概流程是这样的: 最开始的时候我们调用pci_register向PCI总线注册了驱动相关的回调函数, 在pci_register完成后会执行我们自己驱动的e1000_probe函数,这里面最主要的就是初始化一些寄存器,还有就是设置了NAPI,然后就会调用e1000_open函数这里面主要初始化接收环。
数字含义大全上面的流程最主要的就是最后四个函数的处理,在这个循环中不断的收取网卡的数据帧,然后向上层传递。零拷贝需要修改的地方就主要在这里,原来的驱动存放数据帧的内存是通过
__netdev_alloc_skb_ip_align函数分配的,在e1000_clean_rx_irq函数中向协议栈的上层传递
也就是说已经脱离了驱动相关的部分,当数据到达用户层后,这块空间由上层释放,这也是为什么说NAPI比传统的中断更有效率的原因,当执行NAPI的过程中会关中断,但是硬件会继续收数据到我们事先分配好的环形缓冲区中,这也是为什么我们必须先分配一定数量的环形缓冲区,不然的话在我们执行NAPI这段时间来的数据就会丢失。由于传统的中断是每接受到一个数据包就产生一次中断,如果流量很大的话,CPU负荷较重基本都在处理中断。 现在的NAPI每次处理的数据帧数我们可以自己设定,在本驱动中为64叫做权值,在e1000_probe函数中初始化的netif_napi_add(netdev, &adapter->napi, e1000e_poll, 64); 也就是说每次NAPI可以处理64个数据帧,当数据帧的个数大于64的时候会执行多次NAPI,如果小于64执行完NAPI后会开中断,把任务从NAPI链上移除,下次中断来的时候又会关中断, 添加任务,数据向协议栈上层传递,不停地循环。
我们现在主要是把原来DMA到内存的数据, 替换成由我们自己申请的buffer中, 然后我们自己来管理这块buffer,由我们自己来释放这块buffer,我们就可以把我们自己管理的缓冲区映射到用户空间。这样就不需要经过协议栈,就可以直接取得原始的数据帧。
主要添加的文件:
   
Mem_poll.c
主要完成: 1. 申请一定数量的缓冲区 2. 把这些缓冲区组织成链表的形式 3. 提供给驱动申请缓冲区的接口 4. 提供给驱动释放缓冲区的接口 5. 把这些缓冲区映射到用户空间
Sniffer.c
日剧双男主大尺推荐最火电视剧主要实现了把这些用户层和内核的接口及其直接把数据帧从内核中取走,修改相关的指针。
现在来看看mem_poll.c的实现:
先来看看一个重要的数据结构,mem_poll.c主要是围绕这个数据结构展开(在mem_poll.h中)
我们把申请到得缓冲区组织成一个链表,链表的每个节点我们来装一个数据帧,大小为2048字节,当然这个节点的开始一段有很小一部分的管理结构大小为sizeof(struct frame_format)

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。