0. Linux内核网络数据包处理流程 - 网络硬件
网卡工作在物理层和数据链路层,主要由PHY/MAC芯片、Tx/Rx FIFO、DMA等组成,其中网线通过变压器接PHY芯片、PHY芯片通过MII接MAC芯片、MAC芯片接PCI总线
PHY/MAC芯片
PHY芯片主要负责:CSMA/CD、模数转换、编解码、串并转换
MAC芯片主要负责:
- 比特流和帧的转换:7字节的前导码Preamble和1字节的帧首定界符SFD
- CRC校验
- Packet Filtering:L2 Filtering、VLAN Filtering、Manageability / Host Filtering
Intel的千兆网卡以82575/82576为代表、万兆网卡以82598/82599为代表
1. Linux内核网络数据包处理流程 - 网卡驱动
网卡驱动ixgbe初始化
网卡驱动为每个新的接口在一个全局的网络设备列表里插入一个数据结构.每个接口由一个结构 net_device
项来描述, 它在<linux/netdevice.h>
里定义。该结构必须动态分配。
每个网卡,无论是物理还是虚拟的网卡,都必须有一个:net_device
,这个struct是在网卡驱动中分配创建的,不通的网卡,对应厂商不同的驱动,那么看看ixgbe的驱动初始化; 创建net_device
的函数是: alloc_etherdev
, 或者: alloc_etherdev_mq
https://www.cnblogs.com/lidp/archive/2009/05/13/1697981.html
pci设备:
在内核中,一个PCI设备,使用struct pci_driver
结构来描述, 因为在系统引导的时候,PCI设备已经被识别,当内核发现一个已经检测到的设备同驱动注册的id_table
中的信息相匹配时,
它就会触发驱动的probe
函数,
比如,看看ixgbe 驱动:
static struct pci_driver ixgb_driver = {
.name = ixgb_driver_name,
.id_table = ixgb_pci_tbl,
.probe = ixgb_probe,
.remove = ixgb_remove,
.err_handler = &ixgb_err_handler
};
#vim drivers/net/ethernet/intel/ixgbe/ixgbe_main.c
module_init
ixgbe_init_module
pci_register_driver
当probe
函数被调用,证明已经发现了我们所支持的网卡,这样,就可以调用register_netdev
函数向内核注册网络设备了,注册之前,一般会调用alloc_etherdev
分配一个net_device
,然后初始化它的重要成员。
ixgbe_probe
struct net_device *netdev;
struct pci_dev *pdev;
pci_enable_device_mem(pdev);
pci_request_mem_regions(pdev, ixgbe_driver_name);
pci_set_master(pdev);
pci_save_state(pdev);
netdev = alloc_etherdev_mq(sizeof(struct ixgbe_adapter), indices);// 这里分配struct net_device
alloc_etherdev_mqs
alloc_netdev_mqs(sizeof_priv, "eth%d", NET_NAME_UNKNOWN, ether_setup, txqs, rxqs);
ether_setup // Initial struct net_device
SET_NETDEV_DEV(netdev, &pdev->dev);
adapter = netdev_priv(netdev);
refs: https://blog.csdn.net/shallnet/article/details/25470775
alloc_etherdev_mqs() -> ether_setup()
void ether_setup(struct net_device *dev)
{
dev->header_ops = ð_header_ops;
dev->type = ARPHRD_ETHER;
dev->hard_header_len = ETH_HLEN;
dev->min_header_len = ETH_HLEN;
dev->mtu = ETH_DATA_LEN;
dev->addr_len = ETH_ALEN;
dev->tx_queue_len = 1000; /* Ethernet wants good queues */
dev->flags = IFF_BROADCAST|IFF_MULTICAST;