r8169驱动源码阅读记录

源码地址:linux-4.19.90\drivers\net\ethernet\realtek\r8169.c
源码阅读环境:Windows 搭建 opengrok|极客教程
在线阅读网站:bootlin

注意:为把握函数主要功能,我忽略了许多出错处理的代码

初始化

阅读网卡驱动源码第一步,简要看一下发送描述符,接收描述符,发送缓存区,接收缓冲区定义与初始化
RTL8169数据手册
第九节介绍发送描述符与接收描述符
发送描述符

接收描述符

1
2
3
4
5
6
7
8
9
10
11
12
// 发送描述符
596 struct TxDesc {
597 __le32 opts1;
598 __le32 opts2;
599 __le64 addr; // 小端地址
600 };
// 接收描述符
602 struct RxDesc {
603 __le32 opts1;
604 __le32 opts2;
605 __le64 addr; // 小端地址
606 };

手册中写明了描述符的大小与字节对齐要求

Each descriptor consists of 4 consecutive double words. The start address of each descriptor group should be 256-byte alignment.


轻松记住大端小端的含义(附对大端和小端的解释)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
// 缓冲区相关定义
650 struct rtl8169_private {
657 u32 cur_rx; /* Index into the Rx descriptor buffer of next Rx pkt. */
658 u32 cur_tx; /* Index into the Tx descriptor buffer of next Rx pkt. */
659 u32 dirty_tx;
660 struct rtl8169_stats rx_stats;
661 struct rtl8169_stats tx_stats;
662 struct TxDesc *TxDescArray; /* 256-aligned Tx descriptor ring */
663 struct RxDesc *RxDescArray; /* 256-aligned Rx descriptor ring */
664 dma_addr_t TxPhyAddr; // 总线地址
665 dma_addr_t RxPhyAddr;
666 void *Rx_databuff[NUM_RX_DESC]; /* Rx data buffers,用来到达的数据包*/
667 struct ring_info tx_skb[NUM_TX_DESC]; /* Tx data buffers,用于存放skb指针 */
715 };
608 struct ring_info { // 保存sk_buff指针,留待之后释放
609 struct sk_buff *skb;
610 u32 len;
611 u8 __pad[sizeof(void *) - sizeof(u32)];
612 };

// ring buffer初始化
74 #define R8169_REGS_SIZE 256
75 #define R8169_RX_BUF_SIZE (SZ_16K - 1)
76 #define NUM_TX_DESC 64 /* Number of Tx descriptor registers */
77 #define NUM_RX_DESC 256U /* Number of Rx descriptor registers */
78 #define R8169_TX_RING_BYTES (NUM_TX_DESC * sizeof(struct TxDesc))
79 #define R8169_RX_RING_BYTES (NUM_RX_DESC * sizeof(struct RxDesc))
6860 /*
6861 * Rx and Tx descriptors needs 256 bytes alignment.
6862 * dma_alloc_coherent provides more.
6863 */
6864 tp->TxDescArray = dma_alloc_coherent(&pdev->dev, R8169_TX_RING_BYTES,
6865 &tp->TxPhyAddr, GFP_KERNEL);
6869 tp->RxDescArray = dma_alloc_coherent(&pdev->dev, R8169_RX_RING_BYTES,
6870 &tp->RxPhyAddr, GFP_KERNEL);

// data buffer初始化
4313 static void rtl8169_init_ring_indexes(struct rtl8169_private *tp)
4314 {
4315 tp->dirty_tx = tp->cur_tx = tp->cur_rx = 0;
4316 }

5950 static inline void rtl8169_mark_as_last_descriptor(struct RxDesc *desc)
5951 {
5952 desc->opts1 |= cpu_to_le32(RingEnd);
5953 }

5978 static int rtl8169_init_ring(struct rtl8169_private *tp)
5979 {
5980 rtl8169_init_ring_indexes(tp); // 索引初始化
5981
5982 memset(tp->tx_skb, 0, sizeof(tp->tx_skb)); // 重置发送数据缓冲区
5983 memset(tp->Rx_databuff, 0, sizeof(tp->Rx_databuff)); // 重置接收数据缓冲区
5984
5985 return rtl8169_rx_fill(tp); // 填充接收描述符与接收数据缓冲区
5986 }

5955 static int rtl8169_rx_fill(struct rtl8169_private *tp)
5956 {
5957 unsigned int i;
5958
5959 for (i = 0; i < NUM_RX_DESC; i++) {
5960 void *data;
5961
5962 data = rtl8169_alloc_rx_data(tp, tp->RxDescArray + i); // 为接收描述符申请DMA内存空间
5967 tp->Rx_databuff[i] = data; // 记录内存地址
5968 }
5969
5970 rtl8169_mark_as_last_descriptor(tp->RxDescArray + NUM_RX_DESC - 1); // 标记为ring buffer末尾
5971 return 0;
5976 }
5902 static struct sk_buff *rtl8169_alloc_rx_data(struct rtl8169_private *tp,
5903 struct RxDesc *desc)
5904 {
5905 void *data;
5906 dma_addr_t mapping;
5907 struct device *d = tp_to_dev(tp);
5908 int node = dev_to_node(d);
5910 data = kmalloc_node(R8169_RX_BUF_SIZE, GFP_KERNEL, node); // 申请内存空间,返回CPU地址
5921 mapping = dma_map_single(d, rtl8169_align(data), R8169_RX_BUF_SIZE,
5922 DMA_FROM_DEVICE); // 建立流式DMA映射,返回总线地址
5929 desc->addr = cpu_to_le64(mapping); // 填充接收描述符
5930 rtl8169_mark_to_asic(desc);
5931 return data;
5936 }

可以看到TxDescArray RxDescArray使用了一致性DMA映射(dma_alloc_coherent),其中的数据存放采用流式DMA映射(DMA_FROM_DEVICE)。一致性DMA映射与流式DMA映射的不同见以下博客
一致性DMA映射与流式DMA映射
dma_addr_t与流式映射和一致性映射
一致性DMA映射不使用Cache,流式DMA映射谨慎地使用Cache

x86_64硬件保证了DMA一致性,无需考虑一致性DMA映射与流式DMA映射。
流式DMA映射根据数据方向对cache进行”flush/invalid”,既保证了数据一致性,也避免了完全关闭cache带来的性能影响。既然如此,为什么不抛弃一致性DMA映射,全面拥抱“更强大”的流式DMA映射呢?
考虑如下情况:当CPU和DMA需要频繁的操作一块内存区域的时候,如果采用流式DMA映射的话,需要频繁的”cache flush/invalid”操作(没有cache hit或者write hit的话,cache存在的意义就不大了),而刷cache是比较耗时的,就会导致开销比较大。这个时候,更适合采用一致性DMA映射。

接下来阅读寄存器映射相关代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
650  struct rtl8169_private {
651 void __iomem *mmio_addr; /* memory map physical address */
715 };
83 /* write/read MMIO register */
84 #define RTL_W8(tp, reg, val8) writeb((val8), tp->mmio_addr + (reg))
85 #define RTL_W16(tp, reg, val16) writew((val16), tp->mmio_addr + (reg))
86 #define RTL_W32(tp, reg, val32) writel((val32), tp->mmio_addr + (reg))
87 #define RTL_R8(tp, reg) readb(tp->mmio_addr + (reg))
88 #define RTL_R16(tp, reg) readw(tp->mmio_addr + (reg))
89 #define RTL_R32(tp, reg) readl(tp->mmio_addr + (reg))
// 一些寄存器地址
247 enum rtl_registers {
248 MAC0 = 0, /* Ethernet hardware address. */
249 MAC4 = 4,
250 MAR0 = 8, /* Multicast filter. */
251 CounterAddrLow = 0x10,
252 CounterAddrHigh = 0x14,
253 TxDescStartAddrLow = 0x20,
254 TxDescStartAddrHigh = 0x24,
255 TxHDescStartAddrLow = 0x28,
256 TxHDescStartAddrHigh = 0x2c,
257 FLASH = 0x30,
258 ERSR = 0x36,
259 ChipCmd = 0x37,
260 TxPoll = 0x38,
261 IntrMask = 0x3c,
262 IntrStatus = 0x3e,
320 };

其中__iomem是linux2.6.9内核中加入的特性。用来个表示指针是指向一个I/O的内存空间,主要是为了驱动程序的通用性考虑。由于不同的CPU体系结构对I/O空间的表示可能不同,当使用__iomem时,编译器会忽略对变量的检查(因为用的是void __iomem)。若要对它进行检查,当__iomem的指针和正常的指针混用时,就会发出一些警告。

寄存器内存映射代码如下:

1
2
3
4
5
6
7
8
9
7489  	/* use first MMIO region */
7490 region = ffs(pci_select_bars(pdev, IORESOURCE_MEM)) - 1; // 找到第一个IORESOURCE_MEM资源
7496 /* check for weird/broken PCI region reporting */
7497 if (pci_resource_len(pdev, region) < R8169_REGS_SIZE) { // 查看该BAR空间大小,需大于网卡寄存器大小
7498 dev_err(&pdev->dev, "Invalid PCI region size(s), aborting\n");
7499 return -ENODEV;
7500 }
7502 rc = pcim_iomap_regions(pdev, BIT(region), MODULENAME); // 进行内存映射
7508 tp->mmio_addr = pcim_iomap_table(pdev)[region]; // 记录映射后内存地址

其中一些内核函数的实现源码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
5664  int pci_select_bars(struct pci_dev *dev, unsigned long flags)	// 资源有效则相应位置1
5665 {
5666 int i, bars = 0;
5667 for (i = 0; i < PCI_NUM_RESOURCES; i++)
5668 if (pci_resource_flags(dev, i) & flags)
5669 bars |= (1 << i);
5670 return bars;
5671 }
13 static inline int ffs(int x) // 找到整数中的第一个置位值(也就是bit为1的位)
14 {
15 int r = 1;
16
17 if (!x)
18 return 0;
19 if (!(x & 0xffff)) {
20 x >>= 16;
21 r += 16;
22 }
23 if (!(x & 0xff)) {
24 x >>= 8;
25 r += 8;
26 }
27 if (!(x & 0xf)) {
28 x >>= 4;
29 r += 4;
30 }
31 if (!(x & 3)) {
32 x >>= 2;
33 r += 2;
34 }
35 if (!(x & 1)) {
36 x >>= 1;
37 r += 1;
38 }
39 return r;
40 }
6 #define BIT(nr) (1UL << (nr))
370 int pcim_iomap_regions(struct pci_dev *pdev, int mask, const char *name) // 映射对应资源
371 {
372 void __iomem * const *iomap;
373 int i, rc;
374
375 iomap = pcim_iomap_table(pdev);
376 if (!iomap)
377 return -ENOMEM;
378
379 for (i = 0; i < DEVICE_COUNT_RESOURCE; i++) {
380 unsigned long len;
381
382 if (!(mask & (1 << i)))
383 continue;
384
386 len = pci_resource_len(pdev, i);
390 rc = pci_request_region(pdev, i, name);
395 if (!pcim_iomap(pdev, i, 0))
396 goto err_region;
397 }
398
399 return 0;
400
// 其中pcim_iomap函数实现如下:将映射后内存保存tbl中,可由pcim_iomap_table函数取出
321 void __iomem *pcim_iomap(struct pci_dev *pdev, int bar, unsigned long maxlen)
322 {
323 void __iomem **tbl;
327 tbl = (void __iomem **)pcim_iomap_table(pdev);
331 tbl[bar] = pci_iomap(pdev, bar, maxlen);
332 return tbl[bar];
333 }

linux驱动 关于资源resource

发包

然后看网络设备的操作函数,简要分析发包过程

1
2
3
4
5
6
7193  static const struct net_device_ops rtl_netdev_ops = {
7194 .ndo_open = rtl_open, // 打开设备时调用,编写网络设备硬件初始化的相关代码
7195 .ndo_stop = rtl8169_close,// 关闭设备时调用
7197 .ndo_start_xmit = rtl8169_start_xmit, // 启动网络数据包传输的方法
7198 .ndo_tx_timeout = rtl8169_tx_timeout, // 超时处理函数
7210 };

1、网卡驱动创建tx descriptor ring(一致性DMA内存),将tx descriptor ring的总线地址写入网卡寄存器
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释放数据缓存区中的数据包

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
// 将ring buffer总线地址写入网卡寄存器
4569 static void rtl_set_rx_tx_desc_registers(struct rtl8169_private *tp)
4570 {
4571 /*
4572 * Magic spell: some iop3xx ARM board needs the TxDescAddrHigh
4573 * register to be written before TxDescAddrLow to work.
4574 * Switching from MMIO to I/O access fixes the issue as well.
4575 */
4576 RTL_W32(tp, TxDescStartAddrHigh, ((u64) tp->TxPhyAddr) >> 32);
4577 RTL_W32(tp, TxDescStartAddrLow, ((u64) tp->TxPhyAddr) & DMA_BIT_MASK(32));
4578 RTL_W32(tp, RxDescAddrHigh, ((u64) tp->RxPhyAddr) >> 32);
4579 RTL_W32(tp, RxDescAddrLow, ((u64) tp->RxPhyAddr) & DMA_BIT_MASK(32));
4580 }
// 启动数据传输
6279 static netdev_tx_t rtl8169_start_xmit(struct sk_buff *skb,
6280 struct net_device *dev)
6281 {
6282 struct rtl8169_private *tp = netdev_priv(dev);
6283 unsigned int entry = tp->cur_tx % NUM_TX_DESC;
6284 struct TxDesc *txd = tp->TxDescArray + entry; // 数据包插入的起始位置
6285 struct device *d = tp_to_dev(tp);
6286 dma_addr_t mapping;
6287 u32 status, len;
6288 u32 opts[2];
6289 int frags;
6290
6291 if (unlikely(!TX_FRAGS_READY_FOR(tp, skb_shinfo(skb)->nr_frags))) { // 是否有足够多的位置插入数据包
6292 netif_err(tp, drv, dev, "BUG! Tx Ring full when queue awake!\n");
6293 goto err_stop_0;
6294 }
6295
6296 if (unlikely(le32_to_cpu(txd->opts1) & DescOwn)) // 查看当前位置是否占用
6297 goto err_stop_0;
6298
6299 opts[1] = cpu_to_le32(rtl8169_tx_vlan_tag(skb)); // 处理标志位信息
6300 opts[0] = DescOwn;
6301
6302 if (!tp->tso_csum(tp, skb, opts)) { // 校验和信息
6303 r8169_csum_workaround(tp, skb);
6304 return NETDEV_TX_OK;
6305 }
6306
6307 len = skb_headlen(skb); // 线性区数据长度
6308 mapping = dma_map_single(d, skb->data, len, DMA_TO_DEVICE); // 建立流式DMA映射
6315 tp->tx_skb[entry].len = len; // 记录段长度
6316 txd->addr = cpu_to_le64(mapping); // 记录映射后的地址
6317
6318 frags = rtl8169_xmit_frags(tp, skb, opts); // 映射非线性区数据
6319 if (frags < 0)
6320 goto err_dma_1;
6321 else if (frags) // 存在非线性数据则只是数据首段
6322 opts[0] |= FirstFrag;
6323 else { // 不存在非线性数据则既是数据首部又是数据尾部
6324 opts[0] |= FirstFrag | LastFrag;
6325 tp->tx_skb[entry].skb = skb; // 记录skb指针,留待之后释放
6326 }
6327
6328 txd->opts2 = cpu_to_le32(opts[1]);
6329
6330 skb_tx_timestamp(skb); // 获取基于软件的发送时间戳
6331
6332 /* Force memory writes to complete before releasing descriptor */
6333 dma_wmb();
6334
6335 /* Anti gcc 2.95.3 bugware (sic) */
6336 status = opts[0] | len | (RingEnd * !((entry + 1) % NUM_TX_DESC));
6337 txd->opts1 = cpu_to_le32(status);
6338
6339 /* Force all memory writes to complete before notifying device */
6340 wmb();
6341
6342 tp->cur_tx += frags + 1; // 更新下一个插入位置
6343
6344 RTL_W8(tp, TxPoll, NPQ); // 置位TxPoll寄存器的NPQ位,告诉硬件可以发包了
6345
6346 mmiowb();
6347 // 流量控制,如果发送描述符不够的话暂时关闭该发送队列,等资源释放后再开启
6348 if (!TX_FRAGS_READY_FOR(tp, MAX_SKB_FRAGS)) {
6349 /* Avoid wrongly optimistic queue wake-up: rtl_tx thread must
6350 * not miss a ring update when it notices a stopped queue.
6351 */
6352 smp_wmb();
6353 netif_stop_queue(dev); // 停止发包
6354 /* Sync with rtl_tx:
6355 * - publish queue status and cur_tx ring index (write barrier)
6356 * - refresh dirty_tx ring index (read barrier).
6357 * May the current thread have a pessimistic view of the ring
6358 * status and forget to wake up queue, a racing rtl_tx thread
6359 * can't.
6360 */
6361 smp_mb();
6362 if (TX_FRAGS_READY_FOR(tp, MAX_SKB_FRAGS)) // 启动发包
6363 netif_wake_queue(dev);
6364 }
6366 return NETDEV_TX_OK;
6379 }

rtl8169_start_xmit函数的主要功能就是解析skb中的数据,填充发送描述符,而后发包。
发送描述符定义如下:

1
2
3
4
5
596  struct TxDesc {
597 __le32 opts1; // 标志位1
598 __le32 opts2; // 标志位2
599 __le64 addr; // 数据存放地址
600 };

sk_buff结构体结构体示意图如下:

Linux内核中sk_buff结构详解
描述符状态定义如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
// First doubleword为opts1 Second doubleword为opts2
532 enum rtl_desc_bit {
533 /* First doubleword. */
534 DescOwn = (1 << 31), /* Descriptor is owned by NIC */
535 RingEnd = (1 << 30), /* End of descriptor ring */
536 FirstFrag = (1 << 29), /* First segment of a packet */
537 LastFrag = (1 << 28), /* Final segment of a packet */
538 };
/* Generic case. */
541 enum rtl_tx_desc_bit {
542 /* First doubleword. */
543 TD_LSO = (1 << 27), /* Large Send Offload */
544 #define TD_MSS_MAX 0x07ffu /* MSS value */
545
546 /* Second doubleword. */
547 TxVlanTag = (1 << 17), /* Add VLAN tag */
548 };
549
550 /* 8169, 8168b and 810x except 8102e. */
551 enum rtl_tx_desc_bit_0 {
552 /* First doubleword. */
553 #define TD0_MSS_SHIFT 16 /* MSS position (11 bits) */
554 TD0_TCP_CS = (1 << 16), /* Calculate TCP/IP checksum */
555 TD0_UDP_CS = (1 << 17), /* Calculate UDP/IP checksum */
556 TD0_IP_CS = (1 << 18), /* Calculate IP checksum */
557 };
558
559 /* 8102e, 8168c and beyond. */
560 enum rtl_tx_desc_bit_1 {
561 /* First doubleword. */
562 TD1_GTSENV4 = (1 << 26), /* Giant Send for IPv4 */
563 TD1_GTSENV6 = (1 << 25), /* Giant Send for IPv6 */
564 #define GTTCPHO_SHIFT 18
565 #define GTTCPHO_MAX 0x7fU
566
567 /* Second doubleword. */
568 #define TCPHO_SHIFT 18
569 #define TCPHO_MAX 0x3ffU
570 #define TD1_MSS_SHIFT 18 /* MSS position (11 bits) */
571 TD1_IPv6_CS = (1 << 28), /* Calculate IPv6 checksum */
572 TD1_IPv4_CS = (1 << 29), /* Calculate IPv4 checksum */
573 TD1_TCP_CS = (1 << 30), /* Calculate TCP/IP checksum */
574 TD1_UDP_CS = (1 << 31), /* Calculate UDP/IP checksum */
575 };

对照手册上的描述符定义图看

rtl8169_start_xmit函数涉及到的一些函数实现如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
60  #define TX_SLOTS_AVAIL(tp) \
61 (tp->dirty_tx + NUM_TX_DESC - tp->cur_tx)
63 /* A skbuff with nr_frags needs nr_frags+1 entries in the tx queue */
64 #define TX_FRAGS_READY_FOR(tp,nr_frags) \ // +1是因为线性区数据占用一个位置
65 (TX_SLOTS_AVAIL(tp) >= (nr_frags + 1))

1944 static inline unsigned int skb_headlen(const struct sk_buff *skb) // 线性区数据长度
1945 {
1946 return skb->len - skb->data_len;
1947 }
// 映射非线性区数据,和映射线性区数据类似的操作
6059 static int rtl8169_xmit_frags(struct rtl8169_private *tp, struct sk_buff *skb,
6060 u32 *opts)
6061 {
6062 struct skb_shared_info *info = skb_shinfo(skb);
6063 unsigned int cur_frag, entry;
6064 struct TxDesc *uninitialized_var(txd);
6065 struct device *d = tp_to_dev(tp);
6066
6067 entry = tp->cur_tx;
6068 for (cur_frag = 0; cur_frag < info->nr_frags; cur_frag++) {
6069 const skb_frag_t *frag = info->frags + cur_frag;
6070 dma_addr_t mapping;
6071 u32 status, len;
6072 void *addr;
6073
6074 entry = (entry + 1) % NUM_TX_DESC;
6075
6076 txd = tp->TxDescArray + entry;
6077 len = skb_frag_size(frag);
6078 addr = skb_frag_address(frag);
6079 mapping = dma_map_single(d, addr, len, DMA_TO_DEVICE); // 建立流式DMA映射
6087 /* Anti gcc 2.95.3 bugware (sic) */
6088 status = opts[0] | len |
6089 (RingEnd * !((entry + 1) % NUM_TX_DESC));
6090
6091 txd->opts1 = cpu_to_le32(status);
6092 txd->opts2 = cpu_to_le32(opts[1]);
6093 txd->addr = cpu_to_le64(mapping);
6094
6095 tp->tx_skb[entry].len = len; // 记录段长度
6096 }
6097
6098 if (cur_frag) {
6099 tp->tx_skb[entry].skb = skb; // 记录skb指针
6100 txd->opts1 |= cpu_to_le32(LastFrag); // 最后一段数据
6101 }
6102
6103 return cur_frag;
6108 }

收包

在阅读收包代码之前简要了解代码中时不时出现的缩写

TSO(TCP Segmentation Offload),是利用网卡对TCP数据包分片,减轻CPU负荷的一种技术,也有人叫 LSO (Large segment offload) ,TSO是针对TCP的,UFO是针对UDP的。如果硬件支持 TSO功能,同时也需要硬件支持的TCP校验计算和分散/聚集 (Scatter Gather) 功能。如果网卡支持TSO/GSO,可以把最多64K大小的TCP payload直接往下传给协议栈,此时IP层也不会进行segmentation,网卡会生成TCP/IP包头和帧头,这样可以offload很多协议栈上的内存操作,节省CPU资源,当然如果都是小包,那么功能基本就没啥用了。
GSO(Generic Segmentation Offload),GSO是TSO的增强 ,GSO不只针对TCP,对任意协议。比TSO更通用,推迟数据分片直至发送到网卡驱动之前,此时会检查网卡是否支持分片功能(如TSO、UFO),如果支持直接发送到网卡,如果不支持就进行分片后再发往网卡。
LRO(Large Receive Offload),通过将接收到的多个TCP数据聚合成一个大的数据包,然后传递给网络协议栈处理,以减少上层协议栈处理 开销,提高系统接收TCP数据包的能力。
GRO(Generic Receive Offload),跟LRO类似,克服了LRO的一些缺点,更通用。后续的驱动都使用GRO的接口,而不是LRO。

简要了解一下NAPI机制,它的核心概念就是不采用中断的方式读取数据,而代之以首先采用中断唤醒数据接收的服务程序,然后 POLL 的方法来轮询数据。

其中poll函数的budget参数用于控制软中断处理的时间
在net_rx_action()函数中还对每一次软中断处理的时间做了限制,这是由两个变量来控制的:

  1. time_limit = jiffies + 2,如果当前时间超过了time_limit,就强制终止此次软中断处理。即时间不能超过2个jiffies。
  2. budget = netdev_budget,每次poll函数返回,budget就减去此次收包数,当budget减到0时,就强制终止此次软中断处理。netdev_budget设置的值为300。
    详见以下博客:
    数据包接收系列 — NAPI的原理和实现
    napi
    Linux网络协议栈:NAPI机制与处理流程分析(图解)

由中断函数出发,分析其收包过程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
6884  	retval = pci_request_irq(pdev, 0, rtl8169_interrupt, NULL, tp,
6885 dev->name); // 注册中断函数,传入tp
7592 netif_napi_add(dev, &tp->napi, rtl8169_poll, NAPI_POLL_WEIGHT); // 注册轮询函数

6620 static irqreturn_t rtl8169_interrupt(int irq, void *dev_instance)
6621 {
6622 struct rtl8169_private *tp = dev_instance;
6623 u16 status = rtl_get_events(tp); // 读取状态寄存器
6624
6625 if (status == 0xffff || !(status & (RTL_EVENT_NAPI | tp->event_slow)))
6626 return IRQ_NONE;
6627
6628 rtl_irq_disable(tp); // 关闭中断
6629 napi_schedule_irqoff(&tp->napi); // 开启调度
6630
6631 return IRQ_HANDLED;
6632 }

代码涉及到的寄存器介绍如下


一些函数实现如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
1331  static u16 rtl_get_events(struct rtl8169_private *tp)
1332 {
1333 return RTL_R16(tp, IntrStatus);
1334 }
1342 static void rtl_irq_disable(struct rtl8169_private *tp)
1343 {
1344 RTL_W16(tp, IntrMask, 0);
1345 mmiowb();
1346 }
447 /**
448 * napi_schedule_irqoff - schedule NAPI poll
449 * @n: NAPI context
450 *
451 * Variant of napi_schedule(), assuming hard irqs are masked.
452 */
453 static inline void napi_schedule_irqoff(struct napi_struct *n)
454 {
455 if (napi_schedule_prep(n))
456 __napi_schedule_irqoff(n);
457 }

中断上半部做的事情较少,分析下半部的轮询函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
6699  static int rtl8169_poll(struct napi_struct *napi, int budget)
6700 {
6701 struct rtl8169_private *tp = container_of(napi, struct rtl8169_private, napi); // 由成员反推出结构体
6702 struct net_device *dev = tp->dev;
6703 u16 enable_mask = RTL_EVENT_NAPI | tp->event_slow;
6704 int work_done;
6705 u16 status;
6706
6707 status = rtl_get_events(tp);
6708 rtl_ack_events(tp, status & ~tp->event_slow); // 确认正常事件
6709
6710 work_done = rtl_rx(dev, tp, (u32) budget); // 接收数据包
6711
6712 rtl_tx(dev, tp); // 释放发送数据缓冲区
6713
6714 if (status & tp->event_slow) { // 存在异常事件
6715 enable_mask &= ~tp->event_slow;
6716
6717 rtl_schedule_task(tp, RTL_FLAG_TASK_SLOW_PENDING); // 进行SLOW_PENDING模式
6718 }
6719
6720 if (work_done < budget) { // 配额未用完
6721 napi_complete_done(napi, work_done);
6722
6723 rtl_irq_enable(tp, enable_mask); // 开启中断
6724 mmiowb();
6725 }
6726
6727 return work_done;
6728 }

其中tp->event_slow成员的初始化代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
7212  static const struct rtl_cfg_info {
7213 void (*hw_start)(struct rtl8169_private *tp);
7214 u16 event_slow;
7215 unsigned int has_gmii:1;
7216 const struct rtl_coalesce_info *coalesce_info;
7217 u8 default_ver;
7218 } rtl_cfg_infos [] = {
7219 [RTL_CFG_0] = {
7220 .hw_start = rtl_hw_start_8169,
7221 .event_slow = SYSErr | LinkChg | RxOverflow | RxFIFOOver,
7222 .has_gmii = 1,
7223 .coalesce_info = rtl_coalesce_info_8169,
7224 .default_ver = RTL_GIGA_MAC_VER_01,
7225 },
7226 [RTL_CFG_1] = {
7227 .hw_start = rtl_hw_start_8168,
7228 .event_slow = SYSErr | LinkChg | RxOverflow,
7229 .has_gmii = 1,
7230 .coalesce_info = rtl_coalesce_info_8168_8136,
7231 .default_ver = RTL_GIGA_MAC_VER_11,
7232 },
7233 [RTL_CFG_2] = {
7234 .hw_start = rtl_hw_start_8101,
7235 .event_slow = SYSErr | LinkChg | RxOverflow | RxFIFOOver |
7236 PCSTimeout,
7237 .coalesce_info = rtl_coalesce_info_8168_8136,
7238 .default_ver = RTL_GIGA_MAC_VER_13,
7239 }
7240 };
7430 const struct rtl_cfg_info *cfg = rtl_cfg_infos + ent->driver_data;
7632 tp->event_slow = cfg->event_slow;

tp->wk.flags成员赋值代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
650  struct rtl8169_private {
687 struct {
688 DECLARE_BITMAP(flags, RTL_FLAG_MAX);
689 struct mutex mutex;
690 struct work_struct work;
691 } wk;
715 };
6852 static int rtl_open(struct net_device *dev)
set_bit(RTL_FLAG_TASK_ENABLED, tp->wk.flags);

6808 static int rtl8169_close(struct net_device *dev)
bitmap_zero(tp->wk.flags, RTL_FLAG_MAX);

4044 static void rtl_schedule_task(struct rtl8169_private *tp, enum rtl_flag flag)
4045 {
4046 if (!test_and_set_bit(flag, tp->wk.flags))
4047 schedule_work(&tp->wk.work);
4048 }
4049

接下来分析关键的两个函数:rtl_rx和rtl_tx

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
6523  static int rtl_rx(struct net_device *dev, struct rtl8169_private *tp, u32 budget)
6524 {
6525 unsigned int cur_rx, rx_left;
6526 unsigned int count;
6527
6528 cur_rx = tp->cur_rx;
6529
6530 for (rx_left = min(budget, NUM_RX_DESC); rx_left > 0; rx_left--, cur_rx++) {
6531 unsigned int entry = cur_rx % NUM_RX_DESC;
6532 struct RxDesc *desc = tp->RxDescArray + entry;
6533 u32 status;
6534
6535 status = le32_to_cpu(desc->opts1);
6536 if (status & DescOwn) // 位置被占用
6537 break;
6538
6539 /* This barrier is needed to keep us from reading
6540 * any other fields out of the Rx descriptor until
6541 * we know the status of DescOwn
6542 */
6543 dma_rmb();
6564 struct sk_buff *skb;
6565 dma_addr_t addr;
6566 int pkt_size;
6567
6568 process_pkt:
6569 addr = le64_to_cpu(desc->addr); // 获取总线地址
6570 if (likely(!(dev->features & NETIF_F_RXFCS)))
6571 pkt_size = (status & 0x00003fff) - 4;
6572 else
6573 pkt_size = status & 0x00003fff;
6574
6586 skb = rtl8169_try_rx_copy(tp->Rx_databuff[entry],
6587 tp, pkt_size, addr); // 根据接收描述符中的数据构建skb
6592
6593 rtl8169_rx_csum(skb, status); // 校验和信息
6594 skb_put(skb, pkt_size); // 向skb尾部添加数据
6595 skb->protocol = eth_type_trans(skb, dev); // 根据包L2层的数据赋值protocol
6596
6597 rtl8169_rx_vlan_tag(desc, skb);
6598
6599 if (skb->pkt_type == PACKET_MULTICAST)
6600 dev->stats.multicast++;
6601
6602 napi_gro_receive(&tp->napi, skb); // 处理skb并提交到上层协议栈
6603
6604 u64_stats_update_begin(&tp->rx_stats.syncp);
6605 tp->rx_stats.packets++;
6606 tp->rx_stats.bytes += pkt_size;
6607 u64_stats_update_end(&tp->rx_stats.syncp);
6608 }
6609 release_descriptor:
6610 desc->opts2 = 0;
6611 rtl8169_mark_to_asic(desc);
6612 }
6613
6614 count = cur_rx - tp->cur_rx; // 处理的数据包数目
6615 tp->cur_rx = cur_rx;
6616
6617 return count;
6618 }

6504 static struct sk_buff *rtl8169_try_rx_copy(void *data,
6505 struct rtl8169_private *tp,
6506 int pkt_size,
6507 dma_addr_t addr)
6508 {
6509 struct sk_buff *skb;
6510 struct device *d = tp_to_dev(tp);
6511
6512 data = rtl8169_align(data); // 数据对齐
6513 dma_sync_single_for_cpu(d, addr, pkt_size, DMA_FROM_DEVICE); // 获取缓冲区所有权
6514 prefetch(data); // 数据预取
6515 skb = napi_alloc_skb(&tp->napi, pkt_size); // 分配skb空间
6516 if (skb)
6517 skb_copy_to_linear_data(skb, data, pkt_size); // 拷贝数据
6518 dma_sync_single_for_device(d, addr, pkt_size, DMA_FROM_DEVICE); // 将缓冲区所有权交还给设备
6519
6520 return skb;
6521 }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
6426  static void rtl_tx(struct net_device *dev, struct rtl8169_private *tp)
6427 {
6428 unsigned int dirty_tx, tx_left;
6429
6430 dirty_tx = tp->dirty_tx;
6431 smp_rmb();
6432 tx_left = tp->cur_tx - dirty_tx;
6433
6434 while (tx_left > 0) {
6435 unsigned int entry = dirty_tx % NUM_TX_DESC;
6436 struct ring_info *tx_skb = tp->tx_skb + entry;
6437 u32 status;
6438
6439 status = le32_to_cpu(tp->TxDescArray[entry].opts1); // 获取发送描述符状态
6440 if (status & DescOwn) // 未发完包,跳出循环
6441 break;
6442
6443 /* This barrier is needed to keep us from reading
6444 * any other fields out of the Tx descriptor until
6445 * we know the status of DescOwn
6446 */
6447 dma_rmb();
6448
6449 rtl8169_unmap_tx_skb(tp_to_dev(tp), tx_skb,
6450 tp->TxDescArray + entry); // 取消内存映射
6451 if (status & LastFrag) { // skb对应发送描述符全部释放,释放该skb
6452 u64_stats_update_begin(&tp->tx_stats.syncp); // 记录统计数据
6453 tp->tx_stats.packets++;
6454 tp->tx_stats.bytes += tx_skb->skb->len;
6455 u64_stats_update_end(&tp->tx_stats.syncp);
6456 dev_consume_skb_any(tx_skb->skb);
6457 tx_skb->skb = NULL;
6458 }
6459 dirty_tx++;
6460 tx_left--;
6461 }
6462
6463 if (tp->dirty_tx != dirty_tx) {
6464 tp->dirty_tx = dirty_tx;
6465 /* Sync with rtl8169_start_xmit:
6466 * - publish dirty_tx ring index (write barrier)
6467 * - refresh cur_tx ring index and queue status (read barrier)
6468 * May the current thread miss the stopped queue condition,
6469 * a racing xmit thread can only have a right view of the
6470 * ring status.
6471 */
6472 smp_mb();
6473 if (netif_queue_stopped(dev) &&
6474 TX_FRAGS_READY_FOR(tp, MAX_SKB_FRAGS)) { //如果之前由于发送描述符不够导致队列关闭的话,重新判断
6475 netif_wake_queue(dev);
6476 }
6477 /*
6478 * 8168 hack: TxPoll requests are lost when the Tx packets are
6479 * too close. Let's kick an extra TxPoll request when a burst
6480 * of start_xmit activity is detected (if it is not detected,
6481 * it is slow enough). -- FR
6482 */
6483 if (tp->cur_tx != dirty_tx)
6484 RTL_W8(tp, TxPoll, NPQ);
6485 }
6486 }
5988 static void rtl8169_unmap_tx_skb(struct device *d, struct ring_info *tx_skb,
5989 struct TxDesc *desc)
5990 {
5991 unsigned int len = tx_skb->len;
5992
5993 dma_unmap_single(d, le64_to_cpu(desc->addr), len, DMA_TO_DEVICE); // 取消DMA流式映射
5994
5995 desc->opts1 = 0x00; // 清空发送描述符
5996 desc->opts2 = 0x00;
5997 desc->addr = 0x00;
5998 tx_skb->len = 0;
5999 }
3527 static inline void dev_consume_skb_any(struct sk_buff *skb)
3528 {
3529 __dev_kfree_skb_any(skb, SKB_REASON_CONSUMED);
3530 }

描述符更改

主要关注描述符中DescOwn位的变更,1代表描述符为网卡所拥有,0表示描述符为驱动所拥有

发送描述符

结构体定义

1
2
3
4
5
596  struct TxDesc {
597 __le32 opts1;
598 __le32 opts2;
599 __le64 addr;
600 };

初始化

没看见清零的代码,不知道dma_alloc_coherent默认分配的内存是否清零,但按照rtl8169_unmap_tx_skb的代码,初始状态应该为全0

发包
网卡驱动进行发包操作,填充发送描述符, 此时将DescOwn位置位,将所有权转移至网卡

1
2
3
4
5
// rtl8169_start_xmit
struct TxDesc *txd = tp->TxDescArray + entry;
txd->addr = cpu_to_le64(mapping);
txd->opts2 = cpu_to_le32(opts[1]);
txd->opts1 = cpu_to_le32(status);

网卡
当网卡成功发送相应数据包后,DMA修改发送描述符,将DescOwn位清零,将所有权转移至驱动

发包成功
相应数据包已发送,重置描述符,此时发送描述符的DescOwn位为0

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// rtl_tx->rtl8169_unmap_tx_skb 
5988 static void rtl8169_unmap_tx_skb(struct device *d, struct ring_info *tx_skb,
5989 struct TxDesc *desc)
5990 {
5991 unsigned int len = tx_skb->len;
5992
5993 dma_unmap_single(d, le64_to_cpu(desc->addr), len, DMA_TO_DEVICE);
5994
5995 desc->opts1 = 0x00;
5996 desc->opts2 = 0x00;
5997 desc->addr = 0x00;
5998 tx_skb->len = 0;
5999 }
6000

接收描述符

结构体定义

1
2
3
4
5
602  struct RxDesc {
603 __le32 opts1;
604 __le32 opts2;
605 __le64 addr;
606 };

初始化
将DescOwn置位,表示网卡拥有该描述符

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// rtl8169_rx_fill
desc->addr = cpu_to_le64(mapping);
5887 static inline void rtl8169_mark_to_asic(struct RxDesc *desc)
5888 {
5889 u32 eor = le32_to_cpu(desc->opts1) & RingEnd;
5890
5891 /* Force memory writes to complete before releasing descriptor */
5892 dma_wmb();
5893
5894 desc->opts1 = cpu_to_le32(DescOwn | eor | R8169_RX_BUF_SIZE);
5895 }
rtl8169_mark_as_last_descriptor(tp->RxDescArray + NUM_RX_DESC - 1);
5950 static inline void rtl8169_mark_as_last_descriptor(struct RxDesc *desc)
5951 {
5952 desc->opts1 |= cpu_to_le32(RingEnd);
5953 }

网卡
网卡接收到数据包,DMA修改接收描述符,将DescOwn清零,将所有权转移至驱动

收包
处理数据包完成网卡驱动重置接收描述符,继续将DescOwn置位,所有权移交网卡

1
2
3
4
// rtl_rx 
6609 release_descriptor:
6610 desc->opts2 = 0;
6611 rtl8169_mark_to_asic(desc);

参考博客

网卡驱动的收发包流程 推荐
轻松记住大端小端的含义(附对大端和小端的解释)
一致性DMA映射与流式DMA映射
dma_addr_t与流式映射和一致性映射
linux驱动 关于资源resource
Linux内核中sk_buff结构详解
Linux 网络子系统
关于网卡特性TSO、UFO、GSO、LRO、GRO
数据包接收系列 — NAPI的原理和实现
napi
Linux网络协议栈:NAPI机制与处理流程分析(图解)