1. 前言
本文主要介绍设备通过 DMA 从 RAM 中读取数据并将其发送到网络,主要分析dev_hard_start_xmit 通过调用 ndo_start_xmit来发送数据的过程。
2. 驱动回调函数注册
驱动程序实现了一系列方法来支持设备操作,例如:
发送数据(ndo_start_xmit)
获取统计信息(ndo_get_stats64)
处理设备 ioctls(ndo_do_ioctl)
这些方法通过一个 struct net_device_ops 实例导出。看igb 驱动程序中这些操作:
static const struct net_device_ops igb_netdev_ops = {
.ndo_open = igb_open,
.ndo_stop = igb_close,
.ndo_start_xmit = igb_xmit_frame,
.ndo_get_stats64 = igb_get_stats64,
/* ... more fields ... */
};
这个 igb_netdev_ops 变量在 igb_probe函数中注册给设备:
static int igb_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
{
/* ... lots of other stuff ... */
netdev->netdev_ops = &igb_netdev_ops;
/* ... more code ... */
}
3. ndo_start_xmit 发送数据
上层的网络栈通过 struct net_device_ops 实例里的回调函数,调用驱动程序来执行各种操作。正如我们之前看到的,qdisc 代码调用 ndo_start_xmit 将数据传递给驱动程序进行发送。对于大多数硬件设备,都是在保持一个锁时调用 ndo_start_xmit 函数。
在 igb 设备驱动程序中,ndo_start_xmit 字段初始化为 igb_xmit_frame 函数,所以接下来从 igb_xmit_frame 开始,查看该驱动程序是如何发送数据的。在 drivers/net/ethernet/intel/igb/igb_main.c中,以下代码在整个执行过程中都 hold 着一个锁:
netdev_tx_t igb_xmit_frame_ring(struct sk_buff *skb,
struct igb_ring *tx_ring)
{
struct igb_tx_buffer *first;
int tso;
u32 tx_flags = 0;
u16 count = TXD_USE_COUNT(skb_headlen(skb));
__be16 protocol = vlan_get_protocol(skb);
u8 hdr_len = 0;
/* need: 1 descriptor per page * PAGE_SIZE/IGB_MAX_DATA_PER_TXD,* + 1 desc for skb_headlen/IGB_MAX_DATA_PER_TXD,* + 2 desc gap to keep tail from touching head,* + 1 desc for context descriptor,* otherwise try next time*/
if (NETDEV_FRAG_PAGE_MAX_SIZE > IGB_MAX_DATA_PER_TXD) {
unsigned short f;
for (f = 0; f < skb_shinfo(skb)->nr_frags; f++)
count += TXD_USE_COUNT(skb_shinfo(skb)->frags[f].size);
} else {
count += skb_shinfo(skb)->nr_frags;
}
函数首先使用 TXD_USER_COUNT 宏来计算发