网卡驱动收发包过程图解

网卡

网卡工作在物理层和数据链路层,主要由PHY/MAC芯片、Tx/Rx FIFO、DMA等组成,其中网线通过变压器接PHY芯片、PHY芯片通过MII接MAC芯片、MAC芯片接PCI总线

PHY芯片主要负责:CSMA/CD、模数转换、编解码、串并转换

MAC芯片主要负责:

1. 比特流和帧的转换:7字节的前导码Preamble和1字节的帧首定界符SFD

2. CRC校验

3. Packet Filtering:L2 Filtering、VLAN Filtering、Manageability / Host Filtering

Intel的千兆网卡以82575/82576为代表、万兆网卡以82598/82599为代表

收发包过程图

ixgbe_adapter包含ixgbe_q_vector数组(一个ixgbe_q_vector对应一个中断),ixgbe_q_vector包含napi_struct

硬中断函数把napi_struct加入CPU的poll_list,软中断函数net_rx_action()遍历poll_list,执行poll函数

网卡驱动收发包过程图解


发包过程

网卡驱动收发包过程图解


1、网卡驱动创建tx descriptor ring(一致性DMA内存),将tx descriptor ring的总线地址写入网卡寄存器TDBA

2、协议栈通过dev_queue_xmit()将sk_buff下送网卡驱动

3、网卡驱动将sk_buff放入tx descriptor ring,更新TDT

4、DMA感知到TDT的改变后,找到tx descriptor ring中下一个将要使用的descriptor

5、DMA通过PCI总线将descriptor的数据缓存区复制到Tx FIFO

6、复制完后,通过MAC芯片将数据包发送出去

7、发送完后,网卡更新TDH,启动硬中断通知CPU释放数据缓存区中的数据包

Tx Ring Buffer

收包过程

网卡驱动收发包过程图解

1、网卡驱动创建rx descriptor ring(一致性DMA内存),将rx descriptor ring的总线地址写入网卡寄存器RDBA

2、网卡驱动为每个descriptor分配sk_buff和数据缓存区,流式DMA映射数据缓存区,将数据缓存区的总线地址保存到descriptor

3、网卡接收数据包,将数据包写入Rx FIFO

4、DMA找到rx descriptor ring中下一个将要使用的descriptor

5、整个数据包写入Rx FIFO后,DMA通过PCI总线将Rx FIFO中的数据包复制到descriptor的数据缓存区

6、复制完后,网卡启动硬中断通知CPU数据缓存区中已经有新的数据包了,CPU执行硬中断函数:

  • NAPI(以e1000网卡为例):e1000_intr() -> __napi_schedule() -> __raise_softirq_irqoff(NET_RX_SOFTIRQ)
  • 非NAPI(以dm9000网卡为例):dm9000_interrupt() -> dm9000_rx() -> netif_rx() -> napi_schedule() -> __napi_schedule() -> __raise_softirq_irqoff(NET_RX_SOFTIRQ)

7、ksoftirqd执行软中断函数net_rx_action():

  • NAPI(以e1000网卡为例):net_rx_action() -> e1000_clean() -> e1000_clean_rx_irq() -> e1000_receive_skb() -> netif_receive_skb()
  • 非NAPI(以dm9000网卡为例):net_rx_action() -> process_backlog() -> netif_receive_skb()

8、网卡驱动通过netif_receive_skb()将sk_buff上送协议栈

Rx Ring Buffer

软件(SW)向从next_to_use开始的N个descriptor补充sk_buff,next_to_use += N,tail = next_to_use - 1(设置网卡寄存器RDT)

硬件(HW)向从head开始的M个descriptor的sk_buff复制数据包并设置DD,head += M

SW将从next_to_clean的开始的L个sk_buff移出Rx Ring Buffer交给协议栈,next_to_clean += L,向从next_to_use开始的L个descriptor补充sk_buff,next_to_use += L,tail = next_to_use - 1

注意:每次补充完sk_buff以后,tail、next_to_use、next_to_clean三者都是紧挨着的

网卡驱动收发包过程图解


中断上下部

网卡驱动收发包过程图解


do_IRQ()是CPU处理硬中断的总入口,在do_IRQ()中调用硬中断函数

网卡驱动收发包过程图解

netif_rx()

在netif_rx()中把skb加入CPU的softnet_data

网卡驱动收发包过程图解

RSS + FDIR

FDIR(Flow Director)的优先级高于RSS(Receive Side Scaling)

RSS通过计算包的五元组(sip、sport、dip、dport、protocol)的hash并取余,得到队列的index,然后将包放入这个队列,实现了数据包在各个队列之间的负载均衡,不过RSS不能保证回包也落在同一个队列上

对称hash(sip/sport和dip/dport交换后hash不变)可以部分解决该问题,但是对于一些需要做NAT的设备(比如负载均衡)就失效了,FDIR可以完全解决该问题,参见https://tech.meituan.com/MGW.html

网卡驱动收发包过程图解

上一篇:第二阶段.Linux系统编程


下一篇:[二]Java虚拟机 jvm内存结构 运行时数据内存 class文件与jvm内存结构的映射 jvm数据类型 虚拟机栈 方法区 堆 含义