一、硬件布局

每个网卡(MAC)都有自己的专用DMA Engine,如上图的 TSEC 和 e1000 网卡intel82546。
上图中的红色线就是以太网数据流,DMA与DDR打交道需要其他模块的协助,如TSEC,PCI controller
以太网数据在 TSEC<-->DDR  PCI_Controller<-->DDR 之间的流动,CPU的core是不需要介入的
只有在数据流动结束时(接收完、发送完),DMA Engine才会以外部中断的方式告诉CPU的core

二、DMA Engine

上面是DMA Engine的框图,以接收为例:
1.在System memory中为DMA开辟一端连续空间,用来BD数组  (一致性dma内存)
  BD是给DMA Engine使用的,所以不同的设备,BD结构不同,但是大致都有状态、长度、指针3个成员。

2.初始化BD数组,status为E,length为0
   在System memory中再开辟一块一块的内存,可以不连续,用来存放以太网包
   将这些内存块的总线地址赋给buf(dma映射)

3.当MAC接收以太网数据流,放在了Rx FIFO中

4.当一个以太网包接收完全后,DMA engine依次做以下事情
    fetch bd:开始一个个的遍历BD数组,直到当前BD状态为Empty为止
    update bd:更新BD状态为Ready
    move data:把数据从Rx FIFO中搬移到System Memory中dma映射的部分
    generate interrupt:数据搬移完了,产生外部中断给cpu core

5.cpu core处理外部中断,此时以太网数据已经在System memory中dma映射的部分了
    解除dma映射,更新bd状态为Empty
    再开辟一端内存,将这块内存的总线地址赋给bd的指针字段

三、内核中DMA相关API

void *dma_alloc_cohrent(struct device *dev, size_t size, dma_addr_t *dma_handle, int flag);
功能:分配一致性dma内存,返回这块内存的虚拟地址EA, 这块内存的物理地址保存在 dma_handle
dev:  NULL也行
size: 分配空间的大小
dma_handle: 用来保存内存的总线地址(物理地址)
注意:一致性DMA映射,BD所占内存就是靠dma_alloc_cohrent来分配的。

dma_addr_t *dma_map_single(struct device *dev, void *buffer, size_t size, enum dma_data_direction);
功能:将一块连续的内存 buffer 映射为DMA内存来使用。映射后,CPU不能再操作这块 buffer
返回:这块buffer的总线地址(物理地址)
dev: NULL也行
buffer: 一块连续内存的虚拟地址EA
size: 连续内存的大小
dma_data_direction: dma数据流的方向
注意:流式DMA映射,以太网包所占内存先通过kmalloc来分配,然后通过dma_map_single来映射给bd的

四、e1000驱动中的DMA

网卡驱动中使用DMA的套路差不多都一样,以e1000驱动为例讲一下(TSEC驱动的dma见这里

4.1 加载e1000网卡驱动

e1000_probe(){                        //主要是初始化钩子函数
     netdev = alloc_etherdev(sizeof(struct e1000_adapter));
    netdev->open = &e1000_open;       //重要
    netdev->stop = &e1000_close;
    netdev->hard_start_xmit = &e1000_xmit_frame;
    netdev->get_stats = &e1000_get_stats;
    netdev->set_multicast_list = &e1000_set_multi;
    netdev->set_mac_address = &e1000_set_mac;
    netdev->change_mtu = &e1000_change_mtu;
    netdev->do_ioctl = &e1000_ioctl;
    e1000_set_ethtool_ops(netdev);
    netdev->tx_timeout = &e1000_tx_timeout;
    netdev->watchdog_timeo = 5 * HZ;
#ifdef CONFIG_E1000_NAPI
    netif_napi_add(netdev, &adapter->napi, e1000_clean, 64); //重要
#endif
}

4.1 启动e1000网卡

e1000_open() //当用户敲ifconfig up命令时,最终调用网卡驱动的open函数
     -->e1000_setup_all_rx_resources(adapter)
         -->e1000_setup_rx_resources(adapter, &adapter->rx_ring[i])
               //给rx bd分配一致性dma内存
               rxdr->desc = pci_alloc_consistent(pdev, rxdr->size, &rxdr->dma);
     -->e1000_configure(adapter)
         -->e1000_configure_rx(adapter)
               adapter->clean_rx = e1000_clean_rx_irq;
               adapter->alloc_rx_buf = e1000_alloc_rx_buffers;
         -->调用 adapter->alloc_rx_buf钩子函数,即 e1000_alloc_rx_buffers
                --> skb = netdev_alloc_skb(netdev, bufsz); //调用kmalloc新建一个skb
                    buffer_info->dma = pci_map_single(pdev,
                        skb->data,
                        adapter->rx_buffer_len,
                        PCI_DMA_FROMDEVICE);               //给skb->data建立DMA映射
                    rx_desc->buffer_addr = cpu_to_le64(buffer_info->dma);//初始化bd的buf指针
     -->e1000_request_irq(adapter);
        //挂rx 中断ISR函数为 e1000_intr()

最终bd数据结构应该是下面这个样子
      
4.2 e1000的中断

注意:e1000产生rx中断时,以太网数据包已经在系统内存中,即在skb->data里面
下面的中断处理过程就简略了,详细的看这里
do_IRQ()
{
    中断上半部
       调用e1000网卡的rx中断函数 e1000_intr()
          触发软中断 (使用NAPI的话)
    中断下半部
       依次调用软中断的所有handler
       在net_rx_action中最终调用e1000的napi_struct.poll()钩子函数,即e1000_clean
       e1000_clean()最终调用 e1000_clean_rx_irq()
}

e1000_clean_rx_irq()
{
     rx_desc = E1000_RX_DESC(*rx_ring, i); //获取rx bd
     status = rx_desc->status;
     skb = buffer_info->skb;
     buffer_info->skb = NULL;

pci_unmap_single(pdev,                //解除skb->data的DMA映射
                   buffer_info->dma,
                   buffer_info->length,
                   PCI_DMA_FROMDEVICE);
     length = le16_to_cpu(rx_desc->length);
     length -= 4;                          //以太网包的FCS校验就不要了
     skb_put(skb, length);
     skb->protocol = eth_type_trans(skb, netdev);
     netif_receive_skb(skb);               //skb进入协议栈
}

转载自http://blog.chinaunix.net/uid-24148050-id-1667017.html

网络数据包收发流程(三):e1000网卡和DMA的更多相关文章

  1. 网络数据包收发流程(四):协议栈之packet_type

    进入函数netif_receive_skb()后,skb正式开始协议栈之旅.先上图,协议栈大致过程如下所示:跟OSI七层模型不同,linux根据包结构对网络进行分层.比如,arp头和ip头都是紧跟在以 ...

  2. 网络数据包收发流程(二):不配置NAPI的情况

    一.no NAPI 数据结构不配置NAPI的时候,网络设备不使用自己的napi_struct结构,所有网络设备驱动都使用同一个napi_struct,即cpu私有变量__get_cpu_var(sof ...

  3. Linux内核网络数据包处理流程

    Linux内核网络数据包处理流程 from kernel-4.9: 0. Linux内核网络数据包处理流程 - 网络硬件 网卡工作在物理层和数据链路层,主要由PHY/MAC芯片.Tx/Rx FIFO. ...

  4. linux 内核网络数据包接收流程

    转:https://segmentfault.com/a/1190000008836467 本文将介绍在Linux系统中,数据包是如何一步一步从网卡传到进程手中的. 如果英文没有问题,强烈建议阅读后面 ...

  5. Netty 如何高效接收网络数据?一文聊透 ByteBuffer 动态自适应扩缩容机制

    本系列Netty源码解析文章基于 4.1.56.Final版本,公众号:bin的技术小屋 前文回顾 在前边的系列文章中,我们从内核如何收发网络数据开始以一个C10K的问题作为主线详细从内核角度阐述了网 ...

  6. [置顶] 获取网络数据中的数组显示成ListView的简单流程

    首先说一下  这是我自己的个人笔记,如果想看看,不用看细节,可以看流程. 定义一个线程池 ExecutorService pool = Executors.newFixedThreadPool(15) ...

  7. 网络数据包分析 网卡Offload

    http://blog.nsfocus.net/network-packets-analysis-nic-offload/     对于网络安全来说,网络传输数据包的捕获和分析是个基础工作,绿盟科技研 ...

  8. Swift实战-豆瓣电台(三)获取网络数据

    观看地址:http://v.youku.com/v_show/id_XNzMwMzQxMzky.html 这节内容,我们先说了怎么将storyboard中的组件在类中进行绑定.然后写了一个类用来获取网 ...

  9. UNIX网络编程——网络数据包检测

    网络数据包检测 数据包捕获(sniffer):是指在网络上进行数据收集的行为,需要通过网卡来完成. 三种访问方式: BSD Packet Filter(BPF) SVR4 Datalink Provi ...

随机推荐

  1. hadoop2.0初识1.1

    1.伪分布式hdfs文件系统的搭建(单节点文件系统) 1.1.根据上节的讲解,配置主机映射.jdk和解压hadoop压缩包 1.2.配置namenode 在/opt/modules/hadoop-2. ...

  2. Java的final关键字

    使用final关键字做标识有“最终的”含义 final可以修饰类.方法.属性和变量: 修饰类,则该类不允许被继承(即不能有子类) 修饰方法,则该方法不允许被覆盖(重写) 修饰属性,则该属性不会进行隐形 ...

  3. HDU 5775 Bubble Sort(冒泡排序)

    p.MsoNormal { margin: 0pt; margin-bottom: .0001pt; text-align: justify; font-family: Calibri; font-s ...

  4. git学习笔记总结

    git试免费的开源的分布式版本控制系统,github是一个用git做版本控制的项目托管平台.说白了git就是帮忙你管理你开发的代码,代码每次修改的历史,多人更好的一起开发项目. 分布式版本控制系统,每 ...

  5. zoj3430Detect the Virus(ac自动机)

    链接 解码之后是跟普通的自动机求解一下的,只不过解码比较恶心,512=>N>=0 ,所以不能用字符串来存,需要转换成整数来做. #include <iostream> #inc ...

  6. Web1.0、Web2.0、Web3.0的主要区别

    Web1.0:以静态.单向阅读为主,网站内信息可以直接和其他网站信息进行交互,能通过第三方信息平台同时对多家网站信息进行整合使用. Web2.0:以分享为特征的实时网络,用户在互联网上拥有自己的数据, ...

  7. iframe 子页面获取父页面的元素并且控制样式

    父页面的代码 <div id="div5" style="position:relative;height:500px;">             ...

  8. MultiDex到底有多坑

    google为什么要引入MultiDex? dex指令是用16位寄存器来保存dex中的方法数,所以限制了在apk 中最大的方法数为65535,当超过这个最大值在编译的时候会报 方法数超标的错误. 如何 ...

  9. Linux 指令

    cat cdchmod chowncp cut 名称:cat使用权限:所有使用者使用方式:cat [-AbeEnstTuv] [--help] [--version] fileName说明:把档案串连 ...

  10. Asp.net图片文件上传

    对课本上的代码进行了一点的优化 1.获取文件的名称和文件的后缀名 引用了System.IO, 用Path.GetFileNamehe()取得文件名和Path.GetExtension获取文件的后缀 2 ...