介绍数据包收包过程,有助于我们了解Linux内核网络设备在数据收包过程中的位置,下面从宏观的角度介绍数据包从被网卡接收到进入 socket 接收队列的整个过程:
ring buffer
ksoftirqd
软中断调用 NAPI 的poll
函数从ring buffer
收包(poll 函数是网卡驱动在初始化阶段注册的;每个cpu上都运行着一个ksoftirqd
进程,在系统启动期间就注册了)ring buffer
里面对应的内存区域解除映射(unmapped)下面以常见的Intel I350 网卡的驱动 ibg 为例介绍它的工作过程:
驱动会使用module_init
向内核注册一个初始化函数,当驱动被加载时,内核会调用这个函数。在drivers/net/ethernet/intel/igb/igb_main.c
中初始化函数(igb_init_module):
/**
* igb_init_module - Driver Registration Routine
*
* igb_init_module is the first routine called when the driver is
* loaded. All it does is register with the PCI subsystem.
**/
static int __init igb_init_module(void)
{
int ret;
pr_info("%s - version %s\n", igb_driver_string, igb_driver_version);
pr_info("%s\n", igb_copyright);
/* ... */
ret = pci_register_driver(&igb_driver);
return ret;
}
module_init(igb_init_module);
/**
初始化的大部分工作在pci_register_driver
中完成。
Intel I350 网卡是 PCI express
设备。PCI 设备通过PCI Configuration Space
里面的寄存器识别自己。
PCI express 总线是一种完全不同于过去PCI总线的一种全新总线规范,与PCI总线共享并行架构相比,PCI Express总线是一种点对点串行连接的设备连接方式,点对点意味着每一个PCI Express设备都拥有自己独立的数据连接,各个设备之间并发的数据传输互不影响,而对于过去PCI那种共享总线方式,PCI总线上只能有一个设备进行通信,一旦PCI总线上挂接的设备增多,每个设备的实际传输速率就会下降,性能得不到保证。PCI Express以点对点的方式处理通信,每个设备在要求传输数据的时候各自建立自己的传输通道,对于其他设备这个通道是封闭的,这样的操作保证了通道的专有性,避免其他设备的干扰。
当设备驱动编译时,MODULE_DEVICE_TABLE
宏(定义在 include/module.h
) 会导出一个 PCI 设备 ID 列表(a table of PCI device IDs),驱动据此识别它可以控制的设备,内核也会依据这个列表对不同设备加载相应驱动。
igb 驱动的设备表和 PCI 设备 ID 分别见:drivers/net/ethernet/intel/igb/igb_main.c
和drivers/net/ethernet/intel/igb/e1000_hw.h
。
static DEFINE_PCI_DEVICE_TABLE(igb_pci_tbl) = {
{ PCI_VDEVICE(INTEL, E1000_DEV_ID_I354_BACKPLANE_1GBPS) },
{ PCI_VDEVICE(INTEL, E1000_DEV_ID_I354_SGMII) },
{ PCI_VDEVICE(INTEL, E1000_DEV_ID_I354_BACKPLANE_2_5GBPS) },
{ PCI_VDEVICE(INTEL, E1000_DEV_ID_I211_COPPER), board_82575 },
{ PCI_VDEVICE(INTEL, E1000_DEV_ID_I210_COPPER), board_82575 },
{ PCI_VDEVICE(INTEL, E1000_DEV_ID_I210_FIBER), board_82575 },
{ PCI_VDEVICE(INTEL, E1000_DEV_ID_I210_SERDES), board_82575 },
{ PCI_VDEVICE(INTEL, E1000_DEV_ID_I210_SGMII), board_82575 },
{ PCI_VDEVICE(INTEL, E1000_DEV_ID_I210_COPPER_FLASHLESS), board_82575 },
{ PCI_VDEVICE(INTEL, E1000_DEV_ID_I210_SERDES_FLASHLESS), board_82575 },
/* ... */
};
MODULE_DEVICE_TABLE(pci, igb_pci_tbl);static DEFINE_PCI_DEVICE_TABLE(igb_pci_tbl) = {
{ PCI_VDEVICE(INTEL, E1000_DEV_ID_I354_BACKPLANE_1GBPS) },
{ PCI_VDEVICE(INTEL, E1000_DEV_ID_I354_SGMII) },
{ PCI_VDEVICE(INTEL, E1000_DEV_ID_I354_BACKPLANE_2_5GBPS) },
{ PCI_VDEVICE(INTEL, E1000_DEV_ID_I211_COPPER), board_82575 },
{