r8169驱动源码阅读记录

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

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

初始化

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

// 发送描述符
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.

在这里插入图片描述
轻松记住大端小端的含义(附对大端和小端的解释)

// 缓冲区相关定义
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映射。

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

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的指针和正常的指针混用时,就会发出一些警告。

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

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];				// 记录映射后内存地址

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

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

发包

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

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释放数据缓存区中的数据包

// 将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中的数据,填充发送描述符,而后发包。
发送描述符定义如下:

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

sk_buff结构体结构体示意图如下:
在这里插入图片描述
Linux内核中sk_buff结构详解
描述符状态定义如下:

// 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函数涉及到的一些函数实现如下:

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机制与处理流程分析(图解)

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

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  }

代码涉及到的寄存器介绍如下
在这里插入图片描述
在这里插入图片描述
一些函数实现如下:

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  }

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

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成员的初始化代码如下:

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成员赋值代码如下:

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

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  }
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表示描述符为驱动所拥有

发送描述符

结构体定义

596  struct TxDesc {
597  	__le32 opts1;
598  	__le32 opts2;
599  	__le64 addr;
600  };

初始化

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

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

// 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

// 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  

接收描述符

结构体定义

602  struct RxDesc {
603  	__le32 opts1;
604  	__le32 opts2;
605  	__le64 addr;
606  };

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

// 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置位,所有权移交网卡

// 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机制与处理流程分析(图解)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

最佳损友1020

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值