e1000
e1000网络驱动分析
e1000是intel千兆以太网卡的驱动源码。官方关于驱动的使用可以参考如下链接。
https://www.intel.cn/content/www/cn/zh/support/articles/000006866/network-and-i-o/ethernet-products.html
本文主要结合e1000驱动的源码进行分析. 版本是#define DRV_VERSION "7.3.21-k8-NAPI",这个是ubuntu4.4.0-63版本上的内核。代码文件如下几个:
e1000_ethtool.c
e1000.h
e1000_hw.c
e1000_hw.h
e1000_main.c
e1000_osdep.h
e1000_param.c
1.2.1 加载和卸载
驱动加载时候先运行e1000_init_module函数,主要实现注册到PCI子系统中,代码如下。
static int __init e1000_init_module(void)
{
int ret;
pr_info("%s - version %s\n", e1000_driver_string, e1000_driver_version);
pr_info("%s\n", e1000_copyright);
ret = pci_register_driver(&e1000_driver);
if (copybreak != COPYBREAK_DEFAULT) {
if (copybreak == 0)
pr_info("copybreak disabled\n");
else
pr_info("copybreak enabled for "
"packets <= %u bytes\n", copybreak);
}
return ret;
}
module_init(e1000_init_module);
通过调用pci_register_driver函数注册e1000_driver(为pci_driver为驱动主结构)驱动,将其注册到PCI设备的链表中。
具体如下:
pci_register_driver会调用
__pci_register_driver(drivers/pci/pci-driver.c)函数,该函数调用driver_register函数. driver_register会检测是否已在总线上已经注册或是否需要更新,没问题就问题就调用bus_add_driver函数,bus_add_driver将驱动加到总线上,然后调用pci_driver中定义的probe函数。
还有driver_add_groups函数创建组,kobject_uevent函数发送一个uevent通知用户层。
部分原型如下:
int __pci_register_driver(struct pci_driver *drv, struct module *owner,
const char *mod_name)
我们知道在bus_add_driver函数中,会调用pci_driver结构体指向的probe函数。e1000的probe函数是e1000_probe,该函数执行完也就是驱动和设备绑定了, e1000_probe函数完成设备初始化工作,包括pci_dev结构体,操作系统初始化,e1000_adapter结构体配置,和硬件重启工作。设备device也初始化完了。
卸载是e1000_exit_module函数,直接调用pci_unregister_driver,和pci_register_driver函数是对立的。
static void __exit e1000_exit_module(void)
{
pci_unregister_driver(&e1000_driver);
}
module_exit(e1000_exit_module);
驱动支持的参数是copybreak,默认是256.
module_param(copybreak, uint, 0644);
MODULE_PARM_DESC(copybreak,
"Maximum size of packet that is copied to a new buffer on receive");
1.2.2 网络驱动关键数据结构
设计到的主要结构体有pci_dev,e1000_adapter和net_device。
pci_dev结构是网卡的配置空间和I/O与内存区域,net_device结构则向内核提供了操作网卡的抽象接口。
e1000_adapter结构除了体现相应的硬件无关性外,还管理了发送与接收数据包的相应缓冲空间,网卡的物理地址空间映射后的虚拟地址也在此结构中保存。
e1000_adapter结构中的e1000_hw结构主要保存网卡的硬件参数,其值就是通过读取pci_dev的内容获取而来的。
pci_device_id: PCI Device ID Table
e1000_adapter:设备私有数据结构,定义在e1000.h中。
e1000_hw:硬件相关的结构体,定义在e1000_hw.h中。
pci_driver表示pci驱动。
static struct pci_driver e1000_driver = {
.name = e1000_driver_name,//驱动的名字,必须唯一
.id_table = e1000_pci_tbl,//指向pci_device_id表的指针
.probe = e1000_probe,//驱动的probe函数指针,被PCI核心调用。
.remove = e1000_remove,//当有pci_dev从系统移除时候调用函数的指针,或驱动被卸载时
#ifdef CONFIG_PM //如果定义电源管理
/* Power Management Hooks */
.suspend = e1000_suspend,//pci_dev被挂起时调用的指针。
.resume = e1000_resume,//pci_dev恢复时调用的函数指针
#endif
.shutdown = e1000_shutdown,//系统关闭时候调用
.err_handler = &e1000_err_handler//错误处理句柄
};
static const struct pci_ERROR_handlers e1000_err_handler = {
.error_detected = e1000_io_error_detected,
.slot_reset = e1000_io_slot_reset,
.resume = e1000_io_resume,
};
pci_error_handlers结构体是错误发现,插槽重启的函数句柄。
其中net_device_ops的定义如下, 包含了网络设备接口操作函数:
static const struct net_device_ops e1000_netdev_ops = {
.ndo_open = e1000_open,
.ndo_stop = e1000_close,
.ndo_start_xmit = e1000_xmit_frame,
.ndo_get_stats = e1000_get_stats,
.ndo_set_rx_mode = e1000_set_rx_mode,
.ndo_set_mac_address = e1000_set_mac,
.ndo_tx_timeout = e1000_tx_timeout,
.ndo_change_mtu = e1000_change_mtu,
.ndo_do_ioctl = e1000_ioctl,
.ndo_validate_addr = eth_validate_addr,
.ndo_vlan_rx_add_vid = e1000_vlan_rx_add_vid,
.ndo_vlan_rx_kill_vid = e1000_vlan_rx_kill_vid,
#ifdef CONFIG_NET_POLL_controller
.ndo_poll_controller = e1000_netpoll,
#endif
.ndo_fix_features = e1000_fix_features,
.ndo_set_features = e1000_set_features,
};
用来填充net_device结构体。
结构体e1000_buffer是buffer_info缓冲块数组的单元,用与接收从硬件传送过来的数据(是skb的包裹函数)
struct e1000_buffer {
struct sk_buff *skb;
dma_addr_t dma;
struct page *page;
unsigned long time_stamp;
u16 length;
u16 next_to_watch;
unsigned int segs;
unsigned int bytecount;
u16 mAPPed_as_page;
};
结构体e1000_tx_ring指向ring内存的描述符。
struct e1000_tx_ring {
/* pointer to the descriptor ring memory */
void *desc;
/* physical address of the descriptor ring */
dma_addr_t dma;
/* length of descriptor ring in bytes */
unsigned int size;
/* number of descriptors in the ring */
unsigned int count;
/* next descriptor to associate a buffer with */
unsigned int next_to_use;
/* next descriptor to check for DD status bit */
unsigned int next_to_clean;
/* array of buffer information structs */
struct e1000_buffer *buffer_info;
u16 tdh;
u16 tdt;
bool last_tx_tso;
};
借用《linux-千兆网卡驱动实现机制浅析》一张图如下,比较直观的说明了e1000_adaper,e1000_rx_ring,e1000_rx_desc,e1000_buffer结构体之间的关系:
其中缓冲块的初始化由函数e1000_tx_map实现。
1.2.3 设备打开和关闭
根据e1000_netdev_ops结构体中的定义,打开设备使用的函数为e1000_open。当网络接口激活的时候被调用。
关闭函数为e1000_close也定义在e1000_netdev_ops结构体中,用于禁用网络接口。
1.2.4 数据接收和发送
数据传输是net_device接口的关键组件。驱动程序提供一个hard_start_xmit函数,协议层在发送时调用它来向下传递数据包,现在也是定义在net_dev_ops结构体中如下:
static const struct net_device_ops e1000_netdev_ops = {
.ndo_open = e1000_open,
.ndo_stop = e1000_close,
.ndo_start_xmit = e1000_xmit_frame,
.ndo_get_stats = e1000_get_stats,
.ndo_set_rx_mode = e1000_set_rx_mode,
.ndo_set_mac_address = e1000_set_mac,
.ndo_tx_timeout = e1000_tx_timeout,
.ndo_change_mtu = e1000_change_mtu,
.ndo_do_ioctl = e1000_ioctl,
.ndo_validate_addr = eth_validate_addr,
.ndo_vlan_rx_add_vid = e1000_vlan_rx_add_vid,
.ndo_vlan_rx_kill_vid = e1000_vlan_rx_kill_vid,
#ifdef CONFIG_NET_POLL_CONTROLLER
.ndo_poll_controller = e1000_netpoll,
#endif
.ndo_fix_features = e1000_fix_features,
.ndo_set_features = e1000_set_features,
};
可以看到发送函数为e1000_xmit_frame。
现在的中断驱动程序都采用的是NAPI方式,需要提供poll函数,本驱动中是e1000_netpoll轮询函数。
当有新数据包要发送时候,首先上层协议调用e1000_xmit_frame函数,然后在该函数中调用e1000_tx_queue来根据相应的参数找到缓冲块存放,缓冲块中有dma成员,表示该数据包所在的总线地址,控制总线会把内容映射到总线地址,然后由网卡传送出去。
当有新数据包达到时,首先触动中断处理函数e1000_intr,该中断函数会将数据包放在buffer_info的缓冲块中(),就是讲总线地址指向的内容复制到skb中,然后根据skb中的协议将其传给上层协议的接收函数。
1.2.5 个别函数分析说明
e1000_get_hw_dev函数通过结构体e1000_hw来得到net_device.
e1000_request_irq函数,通过request_irq函数申请一个终端号,同时关联终端处理函数e1000_intr。
static int e1000_request_irq(struct e1000_adapter *adapter)
e1000_intr函数为终端处理函数,硬中断。
static irqreturn_t e1000_intr(int irq, void *data)
e1000_free_irq函数,调用free_irq释放中断。
e1000_irq_disable函数禁用中断。
e1000_setup_all_tx_resources函数分配发送描述符。
e1000_setup_all_rx_resources函数分配接收描述符
netif_carrier_on/netif_carrier_off/netif_carrier_ok网卡驱动程序通过来和内核网络子系统传递信息。netif_carrier_on,告诉内核子系统网络链接完整。netif_carrier_off,告诉内核子系统网络断开。netif_carrier_ok,查询网络断开还是链接。以上函数主要是改变net_device dev的state状态来告知内核链路状态的变化。
e1000_power_up_phy函数给物理设备上电。
1.2.6 单独编译e1000驱动模块
路径在drivers下,找到对应驱动如net,例如:
drivers/net/ethernet/intel/e1000
可以单独复制出来,
然后在Makefile中增加如下代码:
KERN_DIR = /usr/src/linux-headers-4.4.0-63-generic
all:
make -C $(KERN_DIR) M=`pwd` modules
clean:
make -C $(KERN_DIR) M=`pwd` modules clean
-C 参数后面加config文件所在的文件夹,也可以是
KERN_DIR = /lib/modules/`uname -r`/build
M:参数后面是要编译的模块
1.2.7 参考
Linux-千兆网卡驱动实现机制浅析 关于PCIE的粗的理解可以查看此文