驱动中有e1000的e1000e两个版本, 差别不大, e1000e使用了msix, 更先进一点点.  比较难懂的都是一些基础系的东西, 如下

1) PCIE的配置空间初始化

2) msix机制及初始化

3) napi机制

4) dma机制

下面一个个回答这些问题,  因为内容实在太多, 没法每个问题将的很清楚, 而且我们这个帖子主要是分析e1000e, 只能每个初步说下机理, 如果需要补充这方面的知识, 自行找其他帖子了解

1) PCIE的配置空间初始化: PCIE卡都遵循一个标准,  x86通过往2个内存地址读写就可以控制IO桥访问一个内部寄存器+一个地址偏移, 就可以读写PCI的配置空间, 操作系统实际上就是用这个机制, 判断卡位是否插上了卡, 卡是否合法, 以及写对应的配置区域(相当于初始化).

    这个内部寄存器, 是每个PCIE卡独有的, 通过跟PCI兴趣小组申请, 并通过物理接线的方式接上的, 所以可以唯一的确定一个卡.

2) msix机制及初始化

OS在初始化配置区的时候, 会根据卡将pci卡的msix起始地址写到pci配置的扩展能力区域,  驱动只需要去读取对应的区域, 像os申请msix向量, 即可使用

msix中断是一种特殊的中断, 不需要中断线, 但需要PCIE具备msix能力, 主机也必须支持apic才可.  当系统初始化时, 同时初始化主机上2个特殊硬件, IOAPIC和LocalAPIC,  在内存虚拟地址中开辟一段内存,  给每个cpu分配中断向量. 后面只要往这个内存上写触发设备信息, 那么就会被内存控制器劫持,  内存控制立即明白这是有外设触发了中断, 通知ioapic发送广播,  当对应的cpu判断对应的向量, 知道这个是要被自己处理, 就会处理这个中断.

3) napi机制

napi也是网络设备的一个机制,  把设备的napi的list挂到系统上, 随即发送一个软中断,  调用一个回调函数

4) dma机制

e1000采用的是自动收发, 就是说数据包从网卡的fifo到skb里面,  或者从skb到网卡的fifo是由dma自动完成的,  在完成后会触发msix中断 

   机制的问题讲完了, 这些是比较难懂的(有些我也是一知半解, 比如msix),  系统都有专门的api可以调用. 但是不理解, 会给你阅读代码造成困难.

 

正式进入代码阅读(按照代码阅读顺序): 

netdev.c

1) e1000_init_module() 只干了一个事情, 注册了一个pci驱动结构体到pci驱动链表, 当pci注册后, 根据pci驱动框架, 匹配成功后自然会执行probe函数

 

2) e1000_probe

   <1> pci_set_dma_mask/pci_set_consistent_dma_mask  协调bus总线宽度

   <2> pci_request_selected_regions_exclusive 霸占虚拟地址映地址

            pci_set_master 设置主,  前两部都是常规操作

           alloc_etherdev 分配并初始化网络设备struct net_device及struct e1000_adapter, 其中net_device主要描述设备驱动相关信息,  struct e1000_adapter,主要描述硬件, 及设备内存, 控制调度等信息

         这2个结构随即被初始化, 初始化的内容非常多, 网卡的收发队列net_queue, 网卡的mac地址链表, name space挂载到内核链表, 报文最大长度, napi, 设备名称,  硬件frame长度, 映射bar0,  分配中断信息, 分配adapter的ring结构, 读取eeprom的信息, 等等...

       这个阶段有几个部分需要注意:

       1)  网络设备基础信息被分配, 但是真正数据传输相关结构和内存没有分配, 中断线没有分配,  也就是说, 这个过程仅仅实例化了一个网卡设备的空壳, 并没有占用实际的硬件资源.

      2) struct net_device的操作函数被初始化, 也就是说, 后面后面网卡执行up和down的时候, 就可直接调用网卡的ops方法 . 这种设计是非常好的, 用的时候分配, 不用的时候不占用资源.

 

3) e1000_open:   当网卡执行up的时候, e1000_open被调用. 

      1> e1000e_setup_tx_resources/e1000e_setup_rx_resources:  分配adapter的buffer_info结构, 共256个,  分配adapter->tx_ring的desc一致性内存, 共256个desc, 并初始化tx_ring.  

         其中desc是真正要送给dma控制器的, 而buffer_info只是一个描述结构体

    2> e1000_configure:

          e1000_set_rx_mode, e1000_restore_vlan, e1000_init_manageability 设置收包模式, 设置管理信息等, 略过.   

         e1000_configure_tx: 重要函数,  设置dma控制的传输地址

         e1000_configure_rx: 重要函数, 设置收包函数内存分配回调函数, 和清理回调函数. 及设置dma控制的传输地址

         adapter->clean_rx = e1000_clean_rx_irq;  收包函数 (umap已收取的报文skb, 并申请新的skb, 做dma map到desc上, 并把报文传递给e1000_receive_skb(内核中上层报文处理函数)
         adapter->alloc_rx_buf = e1000_alloc_rx_buffers;  skb分配和映射函数, 被e1000_clean_rx_irq以及网卡初始化过程调用

         e1000_configure同时调用e1000_alloc_rx_buffers, 分配收函数的skb缓冲区, 直接就分配256个, 同时将skb的物理地址传给dma控制器, 并提示dma可以0处开始收包, 可以一直收256个

     

4) e1000_request_irq : 申请msix中断和常规中断

   看网卡支持那种类型的中断, 我们这里支持最优先的msix中断(在param.c里面配置).    根据adapter->msix_entries的个数, 逐个申请msix中断, 并注册中断向量.

  分别注册了3个中断函数: e1000_intr_msix_rx,    e1000_intr_msix_tx  , e1000_msix_other

   e1000_intr_msix_rx: 调用系统收包函数. 通过调用发送软中断, 通知内核调度网卡napi的poll函数. 调用e1000_clean, 这个函数清理收队列skb的映射信息. 申请同等数量的skb, 同时根据流量, 设置是否卡其常规中断

   e1000_intr_msix_tx: 清理中间信息. 调用e1000_clean_tx_irq释放已经发送完成的skb内存, 解除skb的dma映射

   e1000_configure_msix: 重要函数, 设置dma发送中断的频率以及内容

   常规中断回调函数: e1000_intr, 作用和e1000_intr_msix_rx差不多, 也是唤醒内核收包

 

5) 后面就是一些网卡使能工作, 通知系统开始收包, 可以working了

最后要说明一下的是发包函数, 系统发包调用底层的的e1000_xmit_frame,  这个函数重要的功能就是把要发送的报文地址映射到dma发射区. 通知dma发送

 

收包的大致流程:

1) 申请skb, 把skb映射到dma, 开启dma收包,

2) dma收包后发起中断, 调用, 清理dma映射区, 申请同等数量的skb, 把这些skb重新映射到dma, 相当于把空闲的dma补上

3) 把收到的报文, 丢给系统内核协议栈解析

4) 并根据流量大小,  看是否开启传统中断(传统中断的处理上面有描述)

5) 循环

e1000_intr_msix_rx ->   e1000_clean ->    e1000_clean_rx_irq -> e1000_receive_skb

 

发包的大致流程:

1) send发送到skb

2) 调用底层驱动e1000_xmit_frame发送skb

3) 申请dma映射到dma发送队列, 准备发送

4) 发送完成,发送中断, 调用e1000_intr_msix_tx, 解除skb的dma映射

5) 循环

e1000_xmit_frame-> e1000_tx_map -> e1000_tx_queue ->    e1000_intr_msix_tx -> e1000_clean_tx_irq -> (skb_dma_unmap, dev_kfree_skb_any)

相关数据结构:

 

 

网卡类型e1000半虚拟化怎么设置 e1000e网卡_网卡类型e1000半虚拟化怎么设置