Linux e1000网卡驱动流程

对于各种DMA、NAPI、RFS/RPS、SO_REUSEPORT,如果不了解底层,那么对于做应用层优化也只是空中楼阁。

本文以e1000驱动为例,试图理清网络驱动层的数据流、逻辑流。


关于网卡接收到以太流的DMA过程:
网卡DMA引擎在主存中为DMA开辟一段连续空间存放Buffer Descriptor(ptr/length/status),一致性映射(dma_alloc_cohrent)。
再开辟一段可以不连续的空间存放以太包,流式映射。
MAC接收以太流,放在网卡内部Rx FIFO中。
完整包到了以后遍历Buffer Descriptor,找到可用BD,更新status,拷贝完以太包,再产生中断。
CPU core处理中断,解除DMA映射(ptr=NULL),更新status,重新流式映射(dma_map_single,kmalloc)

驱动:
以e1000网卡为例(E1000_main.c):
驱动函数注册到struct pci_driver e1000_driver中,包括
e1000_probe() //这个函数使能相应的硬件,初始化并且注册新设备。
e1000_remove() //当从系统中删除一个设备或者热插扒设备被拔下时,PCI子系统会调用这个函数。
e1000_suspend() //这个函数在设备在休眠状态和激活状态之间切换时调用。
e1000_resume() //通过这个函数,设备驱动可以生成电源管理信号来激活或者关闭系统。

e1000_netpoll()
  -|e1000_intr()
    -|netif_rx_schedule(将poll_list挂到CPU)
 -|__raise_softirq_irqoff(NET_RX_SOFTIRQ触发接收软中断)

e1000_probe()
  -|e1000_sw_init() //初始化e1000私有数据,rx_buffer_len 2048Byte
  -|netif_napi_add() //

e1000_open()  //启动网卡,通过用户ifconfig up命令
  -|e1000_setup_tx_resources()  //分配tx资源(描述符,一致性DMA)
  -|e1000_setup_rx_resources()   //同上
  -|e1000_up()  //配置MAC地址、配置多播/混杂模式、vlan、发送/接收单元等等,都是操作寄存器
    -|e1000_configure_tx() //配置寄存器
-|e1000_configure_rx()
-|e1000_alloc_rx_buffers()  //为rx_ring中的每一个元素分配一个sk_buff,并为每个skb->data建立流式映射。
-|request_irq(irq, &e1000_intr)  //注册中断号irq和中断服务程序
 
do_IRQ()
  -|e1000_intr()  //中断上半部
  -|触发软中断  //NAPI
  ----------------------
  -|依次调用软中断所有handler
  -|net_rx_action中调用e1000的e1000_clean()-->e1000_clean_tx_irq()/e1000_clean_rx_irq()

e1000_clean()  //e1000中断handler
  -|e1000_clean_tx_irq()
    -|e1000_unmap_and_free_tx_resource()  //此时数据已经发送给网卡tx FIFO了,所以挨个回收DMA ring中的skb->data资源。但是,什么时候再映射?
  -|e1000_clean_rx_irq()
    -|pci_unmap_single()  //对于已经copy以太包的skb->data,解除其DMA映射
    -|skb_put()  //删除以太帧尾4字节
    -|netif_receive_skb() //把以太包交付给上层协议栈
-|e1000_alloc_rx_buffers() //重新映射流式DMA


关于上面的问题,什么时候再映射?

e1000的映射函数是e1000_tx_map(),被e1000_xmit_frame()调用,e1000_xmit_frame()是e1000的hard_start_xmit handler,通过发送报文主函数dev_queue_xmit()调用,而dev_queue_xmit()可能通过QoS层调用hard_start_xmit()或者不使用QoS直接调用hard_start_xmit()。

你可能感兴趣的:(Linux)